commit ed8404c0615ec3d31ed57736d519de31c9b9c742 Author: Jiaqiang Xu Date: Thu Aug 18 17:39:55 2016 +0800 Initial commit of Ccnet server. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4a01889 --- /dev/null +++ b/.gitignore @@ -0,0 +1,90 @@ +*~ +*.bak +*.o +*.exe +cscope* +*# +Makefile.in +net/Makefile +net/.deps +ltmain.sh +libtool +*.lo +*.la +lib/libccnet.a +lib/libcommon.a +lib/ccnetevent.c +lib/ccnetobj.c +lib/event-object.h +lib/ccnet-object.h +net/ccnet-test +install-sh +depcomp +config.guess +config.h +config.log +config.status +config.sub +config.cache +configure +*/.deps +cli/Makefile +cli/ccnet-cli +cli/ccnet-create-group +autom4te* +po/POTFILES +missing +mkinstalldirs +stamp-h1 +*.libs/ +Makefile +aclocal.m4 +gtk/ccnet-gtk +*core +tools/ccnet-init +gtk/ccnet-board +gtk/ccnet-msgview +data/new-conf* +m4/intltool.m4 +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 +ccnet-*.tar.gz +config.h.in +py-compile +python/ccnet/ccnet.c +*.stamp +include/ccnet/buildin-types.h +lib/buildin-types.c +*.pyc +*.tmp.ui +*.defs +*.log +.deps +*.db +cli/ccnet-servtool +cli/ccnet-tool +tests/peer-add/conf1/c882e263e9d02c63ca6b61c68508761cbc74c358.peer +tests/peer-add/conf1/c882e263e9d02c63ca6b61c68508761cbc74c358.user +*.dll +*.aps +*.so +build-stamp +debian/files +debian/seafile +debian/*.substvars +net/daemon/ccnet +net/server/ccnet-server +lib/searpc-marshal.h +lib/searpc-signature.h +lib/rpc_table.stamp +lib/rpc_table.tmp +net/cluster/ccnet-cserver +tests/common-conf.sh +libccnet.pc +demo/ccnet-demo-client +demo/ccnet-demo-server +net/daemon/ccnet-test +/compile diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..2e4b1eb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +sudo: false +language: c +compiler: + - gcc + - clang +addons: + apt: + packages: + - valac + - uuid-dev + - libevent-dev + - re2c + - libjansson-dev +cache: + directories: + - $HOME/downloads +script: + - ./ci/travis.sh diff --git a/HACKING b/HACKING new file mode 100644 index 0000000..f32d4f1 --- /dev/null +++ b/HACKING @@ -0,0 +1,126 @@ + +Terminology +=========== + +* Peer +* Ccnet daemon +* Client +* Service daemon: for example seafile daemon. + + +Peer Transfer Layer +=================== + +Communication between peer is packet oriented. + +The packet structure is defined in `lib/packet.h` as + + struct ccnet_header { + uint8_t version; + uint8_t type; + uint16_t length; /* length of payload */ + uint32_t id; /* for identify a service session id */ + }; + + struct ccnet_packet { + struct ccnet_header header; + char data[0]; + }; + +So the max length of payload is 65535. + + +Service Invocation +================== + +Ccnet provide a service invocation layer upon transfer layer. + +Local Service Invoking +---------------------- + +A local service is provided either by ccnet daemon or a service +daemon. To invoke a local service, client first sends a REQUEST +packet containing the service name and arguments to ccnet +daemon. Ccnet daemon will find the daemon who provide the service and +start the service. This starts a `service session`. A service session +is identified by a unique id. Later communication for this session +using REQUEST and UPDATE packets. These packets constains the unique +id in their header for the partners to identify the session. + + + Client --------------> Ccnet Daemon --------------> Service Daemon + + UPDATE UPDATE + Client --------------> Ccnet Daemon ------------> Service Daemon + <------------- <------------ + RESPONSE RESPONSE + + +Remote Service Invoking +----------------------- + + + remote + Client -----------------------> Ccnet Daemon --------------> Remote Ccnet + + UPDATE UPDATE + Client -----------------------> Ccnet Daemon --------------> Remote Ccnet + <----------------------- <-------------- + RESPONSE RESPONSE + +Note: if is self, remote service invoking will be +automatically turned to local service invoking. + + +Service Registration +-------------------- + +The daemons implements a service by a subclass of `CcnetProcessor` and +registers it to ccnet daemon by calling `ccnet_register_service()`: + + CcnetClient *client; + ccnet_register_service (client, "seafile-rpcserver", + CCNET_TYPE_RPCSERVER_PROC); + +This is used by the seafile daemon to register service +"seafile-rpcserver" to ccnet daemon. + +Inside the ccnet daemon, a service is registered simply by calling + + void + ccnet_proc_factory_register_processor (CcnetProcFactory *factory, + const char *proc_name, + GType type); + + +Processor Management +==================== + +Local Service Invoking +---------------------- + +To support local service invoking, a service proxy processor and a +service stub processor will be started when necessary. The runtime +configuration is as following: + + Local Client Ccnet Daemon Service Daemon + ---------------------------------- + Client Processor ---> | Service Proxy --> Service Stub | --> Daemon Processor + ---------------------------------- + +Remote Service Invoking +----------------------- + +The runtime configuration is as following: + + Local Client Ccnet Daemon Remote Ccnet Daemon + ---------------------------------- + Client Processor ---> | Service Proxy --> Service Stub | --> Daemon Processor + ---------------------------------- + + +Notes when using processor +-------------------------- + +* A class of processor may provide different services, we use 'processor->name' + to remember which service an instance of a processor class provided. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..7b04278 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,255 @@ +This program is released under Affero GPLv3, with the following additional +permission to link with OpenSSL library. + +If you modify this program, or any covered work, by linking or +combining it with the OpenSSL project's OpenSSL library (or a +modified version of that library), containing parts covered by the +terms of the OpenSSL or SSLeay licenses, Seafile Ltd. +grants you additional permission to convey the resulting work. +Corresponding Source for a non-source form of such a combination +shall include the source code for the parts of OpenSSL used as well +as that of the covered work. + +The source code files under 'python' directory is released under +Apache License v2.0. You can find Apache License 2.0 file in that +directory. + +Some individual files have their speicific license: + +* lib/buffer.c is from libevent, so it has a BSD-3-clause license. + + Copyright (c) 2002-2007 Niels Provos + Copyright (c) 2007-2010 Niels Provos and Nick Mathewson + + + GNU AFFERO GENERAL PUBLIC LICENSE + + Version 3, 19 November 2007 + +Copyright © 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/Makefile.am b/Makefile.am new file mode 100644 index 0000000..067c2ed --- /dev/null +++ b/Makefile.am @@ -0,0 +1,22 @@ +## Process this file with automake to produce Makefile.in + + +pcfiles = libccnet.pc +pkgconfig_DATA = $(pcfiles) +pkgconfigdir = $(libdir)/pkgconfig + +SUBDIRS = include lib net tools python + +EXTRA_DIST = install-sh libccnet.pc.in LICENCE.txt + +ACLOCAL_AMFLAGS = -I m4 + +install-data-local: +if MACOS + sed -i '' -e "s|(DESTDIR)|${DESTDIR}|g" $(pcfiles) +else + ${SED} -i "s|(DESTDIR)|${DESTDIR}|g" $(pcfiles) +endif + +dist-hook: + git log --format='%H' -1 > $(distdir)/latest_commit diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..e83621f --- /dev/null +++ b/README.markdown @@ -0,0 +1,40 @@ +Ccnet is a framework for writing networked applications in C. It +provides the following basic services: + +1. Peer identification +2. Connection Management +3. Service invocation +4. Message sending + +In ccnet network, there are two types of nodes, i.e., client and server. +Server has the following functions: + +1. User management +2. Group management + +This repository is the Ccnet server. + +Dependency +========== + +The following packages are required to build ccnet: + + valac >= 0.8 + libsearpc + libmysqlclient-dev for compiling ccnet server + +Compile +======= + +To compile the client components, just + + ./autogen.sh && ./configure && make && make install + +In Mac OS, use + + LDFLAGS="-L/opt/local/lib -L/usr/local/mysql/lib -Xlinker -headerpad_max_install_names" ./configure + +License +======= + +Ccnet server is published under AGPLv3. See LICENSE.txt for details. diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..b5f42ef --- /dev/null +++ b/autogen.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# Run this to generate all the initial makefiles, etc. + +: ${AUTOCONF=autoconf} +: ${AUTOHEADER=autoheader} +: ${AUTOMAKE=automake} +: ${ACLOCAL=aclocal} +if test "$(uname)" != "Darwin"; then + : ${LIBTOOLIZE=libtoolize} +else + : ${LIBTOOLIZE=glibtoolize} +fi +: ${LIBTOOL=libtool} + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +ORIGDIR=`pwd` +cd $srcdir +PROJECT=ccnet +TEST_TYPE=-f +CONFIGURE=configure.ac + +DIE=0 + +($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have autoconf installed to compile $PROJECT." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + + +($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "You must have automake installed to compile $PROJECT." + echo "Get ftp://sourceware.cygnus.com/pub/automake/automake-1.7.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +if test "$(uname)" != "Darwin"; then +(grep "^AC_PROG_LIBTOOL" $CONFIGURE >/dev/null) && { + ($LIBTOOL --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`libtool' installed to compile $PROJECT." + echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.4.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} +fi + +if test "$DIE" -eq 1; then + exit 1 +fi + +dr=`dirname .` +echo processing $dr +aclocalinclude="$aclocalinclude -I m4" + +if test x"$MSYSTEM" = x"MINGW32"; then + aclocalinclude="$aclocalinclude -I /mingw32/share/aclocal" +elif test "$(uname)" = "Darwin"; then + aclocalinclude="$aclocalinclude -I /opt/local/share/aclocal" +fi + + +echo "Creating $dr/aclocal.m4 ..." +test -r $dr/aclocal.m4 || touch $dr/aclocal.m4 +echo "Making $dr/aclocal.m4 writable ..." +test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4 + + +echo "Running $LIBTOOLIZE..." +$LIBTOOLIZE --force --copy + +echo "Running $ACLOCAL $aclocalinclude ..." +$ACLOCAL $aclocalinclude + +echo "Running $AUTOHEADER..." +$AUTOHEADER + +echo "Running $AUTOMAKE --gnu $am_opt ..." +$AUTOMAKE --add-missing --gnu $am_opt + +echo "Running $AUTOCONF ..." +$AUTOCONF diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..6393b2f --- /dev/null +++ b/configure.ac @@ -0,0 +1,348 @@ +dnl Process this file with autoconf to produce a configure script. + + +AC_PREREQ(2.61) +AC_INIT([ccnet], [5.1.4], [daniel.pan@seafile.com]) +AC_CONFIG_SRCDIR([net/server/ccnet-server.c]) +AC_CONFIG_HEADER([config.h]) + +AC_CONFIG_MACRO_DIR([m4]) + +AM_INIT_AUTOMAKE([1.9 foreign]) + +#AC_MINGW32 +AC_CANONICAL_BUILD + +dnl enable the build of share library by default +AC_ENABLE_SHARED + +AC_SUBST(LIBTOOL_DEPS) + +# Checks for programs. +AC_PROG_CC +#AM_C_PROTOTYPES +AC_C_CONST +AC_PROG_MAKE_SET +# AC_PROG_RANLIB +LT_INIT + +# Checks for headers. +AC_CHECK_HEADERS([sys/ioctl.h sys/time.h stdarg.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_SYS_LARGEFILE + +# Checks for library functions. +#AC_CHECK_FUNCS([alarm dup2 ftruncate getcwd gethostbyname gettimeofday memmove memset mkdir rmdir select setlocale socket strcasecmp strchr strdup strrchr strstr strtol uname utime strtok_r sendfile]) + +# check platform +AC_MSG_CHECKING(for WIN32) +if test "$build_os" = "mingw32" -o "$build_os" = "mingw64"; then + bwin32=true + AC_MSG_RESULT(compile in mingw) +else + AC_MSG_RESULT(no) +fi + +AC_MSG_CHECKING(for Mac) +if test "$(uname)" = "Darwin"; then + bmac=true + AC_MSG_RESULT(compile in mac) +else + AC_MSG_RESULT(no) +fi + +AC_MSG_CHECKING(for Linux) +if test "$bmac" != "true" -a "$bwin32" != "true"; then + blinux=true + AC_MSG_RESULT(compile in linux) +else + AC_MSG_RESULT(no) +fi + +AC_ARG_ENABLE(ldap, AC_HELP_STRING([--enable-ldap], [enable LDAP]), + [compile_ldap=$enableval],[compile_ldap="no"]) + +AC_ARG_ENABLE(python, + AC_HELP_STRING([--enable-python],[build ccnet python binding]), + [compile_python=$enableval], + [compile_python=yes]) + +AM_CONDITIONAL([COMPILE_PYTHON], [test "${compile_python}" = "yes"]) + +AM_CONDITIONAL([WIN32], [test "$bwin32" = "true"]) +AM_CONDITIONAL([MACOS], [test "$bmac" = "true"]) +AM_CONDITIONAL([LINUX], [test "$blinux" = "true"]) + + +# check libraries +if test "$bwin32" != true; then + if test "$bmac" = true; then + AC_CHECK_LIB(c, uuid_generate, [echo "found library uuid"], + AC_MSG_ERROR([*** Unable to find uuid_generate in libc]), ) + else + AC_CHECK_LIB(uuid, uuid_generate, [echo "found library uuid"], + AC_MSG_ERROR([*** Unable to find uuid library]), ) + fi +fi + +AC_CHECK_LIB(pthread, pthread_create, [echo "found library pthread"], AC_MSG_ERROR([*** Unable to find pthread library]), ) +AC_CHECK_LIB(sqlite3, sqlite3_open,[echo "found library sqlite3"] , AC_MSG_ERROR([*** Unable to find sqlite3 library]), ) +AC_CHECK_LIB(crypto, SHA1_Init, [echo "found library crypto"], AC_MSG_ERROR([*** Unable to find openssl crypto library]), ) + +PTHREAD_CFLAGS=-pthread +PTHREAD_LIBS=-lpthread +has_winpthread=false +if test "$bwin32" = "true"; then + has_winpthread=false + dnl this will tell us if the implementation of pthread is winpthread + AC_CHECK_LIB(winpthread, pthread_create, has_winpthread=true, + [echo "found library winpthread"], ) + PTHREAD_CFLAGS=-pthread + PTHREAD_LIBS=-lwinpthread +fi + +dnl Saddly, the old mingw gcc doesn't support -pthread flag +if test "$bwin32" = "true" -a "$has_winpthread" != "true"; then + PTHREAD_CFLAGS= + PTHREAD_LIBS=-lpthread +fi + +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_LIBS) + +dnl Do we need to use AX_LIB_SQLITE3 to check sqlite? +dnl AX_LIB_SQLITE3 + +CONSOLE= +if test "$bwin32" = "true"; then + AC_ARG_ENABLE(console, AC_HELP_STRING([--enable-console], [enable console]), + [console=$enableval],[console="yes"]) + if test x${console} != xyes ; then + CONSOLE="-Wl,--subsystem,windows -Wl,--entry,_mainCRTStartup" + fi +fi +AC_SUBST(CONSOLE) + +if test "$bwin32" = true; then + LIB_WS32=-lws2_32 + LIB_GDI32=-lgdi32 + LIB_RT= + LIB_INTL=-lintl + LIBS= + LIB_RESOLV= + LIB_UUID=-lrpcrt4 + LIB_IPHLPAPI=-liphlpapi + LIB_SHELL32=-lshell32 + LIB_PSAPI=-lpsapi + MSVC_CFLAGS=-D__MSVCRT_VERSION__=0x0601 + LIB_DIRWATCH= +elif test "$bmac" = true ; then + LIB_WS32= + LIB_GDI32= + LIB_RT= + LIB_INTL= + LIB_RESOLV=-lresolv + LIB_UUID= + LIB_IPHLPAPI= + LIB_SHELL32= + LIB_PSAPI= + MSVC_CFLAGS= + LIB_DIRWATCH="-framework CoreServices" +else + LIB_WS32= + LIB_GDI32= + LIB_RT= + LIB_INTL= + LIB_RESOLV=-lresolv + LIB_UUID=-luuid + LIB_IPHLPAPI= + LIB_SHELL32= + LIB_PSAPI= + MSVC_CFLAGS= + LIB_DIRWATCH= +fi + +AC_SUBST(LIB_WS32) +AC_SUBST(LIB_GDI32) +AC_SUBST(LIB_RT) +AC_SUBST(LIB_INTL) +AC_SUBST(LIB_RESOLV) +AC_SUBST(LIB_UUID) +AC_SUBST(LIB_IPHLPAPI) +AC_SUBST(LIB_SHELL32) +AC_SUBST(LIB_PSAPI) +AC_SUBST(MSVC_CFLAGS) +AC_SUBST(LIB_DIRWATCH) + +LIBEVENT_REQUIRED=2.0 +# gtk and glib +APPINDICATOR_REQUIRED=0.0.7 +GLIB_REQUIRED=2.16.0 +GTK_REQUIRED=2.16.0 +SEARPC_REQUIRED=1.0 +ZDB_REQUIRED=2.10 +LIBNAUTILUS_EXTENSION_REQUIRED=2.30.1 +SEARPC_REQUIRED=1.0 +CURL_REQUIRED=7.17 + +PKG_CHECK_MODULES(SSL, [openssl]) +AC_SUBST(SSL_CFLAGS) +AC_SUBST(SSL_LIBS) + +PKG_CHECK_MODULES(GLIB2, [glib-2.0 >= $GLIB_REQUIRED]) +AC_SUBST(GLIB2_CFLAGS) +AC_SUBST(GLIB2_LIBS) + +PKG_CHECK_MODULES(GOBJECT, [gobject-2.0 >= $GLIB_REQUIRED]) +AC_SUBST(GOBJECT_CFLAGS) +AC_SUBST(GOBJECT_LIBS) + +PKG_CHECK_MODULES(SEARPC, [libsearpc >= $SEARPC_REQUIRED]) +AC_SUBST(SEARPC_CFLAGS) +AC_SUBST(SEARPC_LIBS) + +PKG_CHECK_MODULES(LIBEVENT, [libevent >= $LIBEVENT_REQUIRED]) +AC_SUBST(LIBEVENT_CFLAGS) +AC_SUBST(LIBEVENT_LIBS) + +if test x${compile_python} = xyes; then + AM_PATH_PYTHON([2.6]) + + if test "$bwin32" = true; then + # set pyexecdir to somewhere like /c/Python26/Lib/site-packages + pyexecdir=${PYTHON_DIR}/Lib/site-packages + pythondir=${pyexecdir} + pkgpyexecdir=${pyexecdir}/${PACKAGE} + pkgpythondir=${pythondir}/${PACKAGE} + + fi # end for bwin32 + +fi + +mysql="yes" +check_mysql_config() +{ + AC_PATH_PROG([MYSQLCONFIG], [mysql_config], [no], [$PATH:/usr/local/bin:/usr/local/mysql/bin]) + if test "x$MYSQLCONFIG" = "xno" + then + AC_MSG_WARN([mysql_config is required to build seafile server with mysql]) + mysql="no" + fi +} +AC_MSG_CHECKING(for mysql) +AC_ARG_WITH([mysql], + AS_HELP_STRING([--with-mysql(=)], + [Path is optional and if given should specify the full path to the MySQL + configure script, mysql_config. E.g. --with-mysql=//mysql_config]), + [ + if test "xno" = "x$with_mysql"; then + AC_MSG_RESULT([no]) + mysql="no" + else + AC_MSG_RESULT([yes]) + AC_CHECK_FILE([$with_mysql], [MYSQLCONFIG=$with_mysql], [check_mysql_config]) + fi + ], + [ + AC_MSG_RESULT([yes]) + check_mysql_config + ]) +if test "xyes" = "x$mysql"; then + tmp_CPPFLAGS=$CPPFLAGS + tmp_LDFLAGS=$LDFLAGS + CPPFLAGS="`$MYSQLCONFIG --include` $CPPFLAGS" + LDFLAGS="`$MYSQLCONFIG --libs` $LDFLAGS" + AC_CHECK_HEADERS([mysql.h]) + if test "xyes" = "x$mysql"; then + echo "found mysql client library" + MYSQL_CFLAGS=`$MYSQLCONFIG --include` + MYSQL_LIBS=`$MYSQLCONFIG --libs` + AC_SUBST(MYSQL_CFLAGS) + AC_SUBST(MYSQL_LIBS) + fi + CPPFLAGS=$tmp_CPPFLAGS + LDFLAGS=$tmp_LDFLAGS +fi + +postgresql="yes" +check_postgres_config() +{ + AC_PATH_PROG([PGCONFIG], [pg_config], [no], [$PATH:/usr/local/bin:/usr/local/pgsql/bin]) + if test "x$PGCONFIG" = "xno" + then + AC_MSG_WARN([pg_config is required to build seafile server with postgresql]) + postgresql="no" + fi +} +AC_MSG_CHECKING(for postgresql) +AC_ARG_WITH([postgresql], + AS_HELP_STRING([--with-postgresql(=)], + [Path is optional and if given should specify the full path to the PostgreSQL + configure script, pg_config. E.g. --with-postgresql=//pg_config]), + [ + if test "xno" = "x$with_postgresql"; then + AC_MSG_RESULT([no]) + postgresql="no" + else + AC_MSG_RESULT([yes]) + AC_CHECK_FILE([$with_postgresql], [PGCONFIG=$with_postgresql],[check_postgres_config]) + fi + ], + [ + AC_MSG_RESULT([yes]) + check_postgres_config + ]) +if test "xyes" = "x$postgresql"; then + tmp_CPPFLAGS=$CPPFLAGS + tmp_LDFLAGS=$LDFLAGS + CPPFLAGS="-I`$PGCONFIG --includedir` $CPPFLAGS" + LDFLAGS="-L`$PGCONFIG --libdir` $LDFLAGS" + AC_CHECK_HEADERS([libpq-fe.h], [], [postgresql="no"]) + if test "xyes" = "x$postgresql"; then + echo "found postgresql client library" + PGSQL_CFLAGS="-I`$PGCONFIG --includedir`" + PGSQL_LIBS="-L`$PGCONFIG --libdir` -lpq" + AC_SUBST(PGSQL_CFLAGS) + AC_SUBST(PGSQL_LIBS) + fi + CPPFLAGS=$tmp_CPPFLAGS + LDFLAGS=$tmp_LDFLAGS +fi + +if test "${compile_ldap}" = "yes"; then + if test "$bwin32" != true; then + AC_CHECK_LIB(ldap, ldap_init, [have_ldap="yes"], + AC_MSG_ERROR([*** Unable to find ldap client library]), ) + + if test "${have_ldap}" = "yes"; then + echo "found ldap client library" + AC_DEFINE([HAVE_LDAP], [1], [Define if ldap library exists.]) + AC_SUBST(LDAP_LIBS, "-lldap -llber") + fi + else + AC_DEFINE([HAVE_LDAP], [1], [Define if ldap library exists.]) + AC_SUBST(LDAP_LIBS, "-lWldap32") + fi + + +fi + +ac_configure_args="$ac_configure_args -q" + +AC_CONFIG_FILES( + Makefile + libccnet.pc + net/Makefile + net/server/Makefile + net/common/Makefile + net/common/db-wrapper/Makefile + lib/Makefile + tools/Makefile + include/Makefile + include/ccnet/Makefile + python/Makefile + python/ccnet/Makefile +) + +AC_OUTPUT diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..377fc62 --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,6 @@ + +SUBDIRS = ccnet + +include_HEADERS = ccnet.h + +ccnetdir = $(includedir)/ccnet diff --git a/include/ccnet.h b/include/ccnet.h new file mode 100644 index 0000000..85e19fa --- /dev/null +++ b/include/ccnet.h @@ -0,0 +1,128 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef _CCNET_H +#define _CCNET_H + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include + +/* mainloop */ + +void ccnet_main (CcnetClient *client); + +typedef void (*RegisterServiceCB) (gboolean success); +void ccnet_register_service (CcnetClient *client, + const char *service, const char *group, + GType proc_type, RegisterServiceCB cb); +gboolean ccnet_register_service_sync (CcnetClient *client, + const char *service, + const char *group); +CcnetClient *ccnet_init (const char *central_config_dir, const char *confdir); + +void ccnet_send_command (CcnetClient *client, const char *command, + SendcmdProcRcvrspCallback cmd_cb, void *cbdata); + +void ccnet_add_peer (CcnetClient *client, const char *id, const char *addr); + +void ccnet_connect_peer (CcnetClient *client, const char *id); +void ccnet_disconnect_peer (CcnetClient *client, const char *id); + +/* client pool */ + +struct CcnetClientPool; +typedef struct CcnetClientPool CcnetClientPool; + +struct CcnetClientPool * +ccnet_client_pool_new (const char *central_config_dir, const char *conf_dir); + +CcnetClient * +ccnet_client_pool_get_client (struct CcnetClientPool *cpool); + +void +ccnet_client_pool_return_client (struct CcnetClientPool *cpool, + CcnetClient *client); + +/* rpc wrapper */ + +/* Create rpc client using a single client for transport. */ +SearpcClient * +ccnet_create_rpc_client (CcnetClient *cclient, const char *peer_id, + const char *service_name); + +/* Create rpc client using client pool for transport. */ +SearpcClient * +ccnet_create_pooled_rpc_client (struct CcnetClientPool *cpool, + const char *peer_id, + const char *service); + +SearpcClient * +ccnet_create_async_rpc_client (CcnetClient *cclient, const char *peer_id, + const char *service_name); + +void ccnet_rpc_client_free (SearpcClient *client); +void ccnet_async_rpc_client_free (SearpcClient *client); + +CcnetPeer *ccnet_get_peer (SearpcClient *client, const char *peer_id); +CcnetPeer *ccnet_get_peer_by_idname (SearpcClient *client, const char *idname); +int ccnet_get_peer_net_state (SearpcClient *client, const char *peer_id); +int ccnet_get_peer_bind_status (SearpcClient *client, const char *peer_id); +int ccnet_peer_is_ready (SearpcClient *client, const char *peer_id); +CcnetPeer *ccnet_get_default_relay (SearpcClient *client); +GList *ccnet_get_peers_by_role (SearpcClient *client, const char *role); + +char *ccnet_get_binding_email (SearpcClient *client, const char *peer_id); +GList *ccnet_get_groups_by_user (SearpcClient *client, const char *user); +GList * +ccnet_get_group_members (SearpcClient *client, int group_id); +int +ccnet_org_user_exists (SearpcClient *client, int org_id, const char *user); + +int +ccnet_get_binding_email_async (SearpcClient *client, const char *peer_id, + AsyncCallback callback, void *user_data); + +char *ccnet_sign_message (SearpcClient *client, const char *message); +int ccnet_verify_message (SearpcClient *client, + const char *message, + const char *sig_base64, + const char *peer_id); + +char * +ccnet_pubkey_encrypt (SearpcClient *client, + const char *msg_base64, + const char *peer_id); + +char * +ccnet_privkey_decrypt (SearpcClient *client, const char *msg_base64); + +char *ccnet_get_config (SearpcClient *client, const char *key); +int ccnet_set_config (SearpcClient *client, const char *key, const char *value); + +void +ccnet_login_to_relay (SearpcClient *client, const char *relay_id, + const char *username, const char *passwd); + +int +ccnet_update_peer_address (SearpcClient *client, const char *peer_id, + const char *addr, int port); + +#endif diff --git a/include/ccnet/Makefile.am b/include/ccnet/Makefile.am new file mode 100644 index 0000000..704cbdd --- /dev/null +++ b/include/ccnet/Makefile.am @@ -0,0 +1,11 @@ + +ccnetdir = $(includedir)/ccnet + +ccnet_HEADERS = ccnet-client.h peer.h proc-factory.h \ + message.h option.h \ + processor.h sendcmd-proc.h \ + mqclient-proc.h invoke-service-proc.h \ + status-code.h cevent.h timer.h ccnet-session-base.h \ + valid-check.h job-mgr.h packet.h \ + async-rpc-proc.h ccnetrpc-transport.h \ + rpcserver-proc.h threaded-rpcserver-proc.h diff --git a/include/ccnet/async-rpc-proc.h b/include/ccnet/async-rpc-proc.h new file mode 100644 index 0000000..341ea3e --- /dev/null +++ b/include/ccnet/async-rpc-proc.h @@ -0,0 +1,35 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_ASYNC_RPC_PROC_H +#define CCNET_ASYNC_RPC_PROC_H + +#include + + +#define CCNET_TYPE_ASYNC_RPC_PROC (ccnet_async_rpc_proc_get_type ()) +#define CCNET_ASYNC_RPC_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_ASYNC_RPC_PROC, CcnetAsyncRpcProc)) +#define CCNET_IS_ASYNC_RPC_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_ASYNC_RPC_PROC)) +#define CCNET_ASYNC_RPC_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_ASYNC_RPC_PROC, CcnetAsyncRpcProcClass)) +#define IS_CCNET_ASYNC_RPC_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_ASYNC_RPC_PROC)) +#define CCNET_ASYNC_RPC_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_ASYNC_RPC_PROC, CcnetAsyncRpcProcClass)) + +typedef struct _CcnetAsyncRpcProc CcnetAsyncRpcProc; +typedef struct _CcnetAsyncRpcProcClass CcnetAsyncRpcProcClass; + +struct _CcnetAsyncRpcProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetAsyncRpcProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_async_rpc_proc_get_type (); + +void ccnet_async_rpc_proc_set_rpc (CcnetAsyncRpcProc *proc, + const char *service, + char *fcall_str, + size_t fcall_len, + void *rpc_priv); +#endif + diff --git a/include/ccnet/ccnet-client.h b/include/ccnet/ccnet-client.h new file mode 100644 index 0000000..c638cb2 --- /dev/null +++ b/include/ccnet/ccnet-client.h @@ -0,0 +1,193 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_CLIENT_SESSION_H +#define CCNET_CLIENT_SESSION_H + + +#define SESSION_ID_LENGTH 40 + + +#include +#include + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#else +#include +#endif + +#include "ccnet-session-base.h" + +#define CCNET_TYPE_CLIENT (ccnet_client_get_type ()) +#define CCNET_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_CLIENT, CcnetClient)) +#define CCNET_IS_CLIENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_CLIENT)) +#define CCNET_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_CLIENT, CcnetClientClass)) +#define CCNET_IS_CLIENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_CLIENT)) +#define CCNET_CLIENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_CLIENT, CcnetClientClass)) + +typedef struct _CcnetClient CcnetClient; +typedef struct _CcnetClientClass CcnetClientClass; + + +struct _CcnetPeer; +struct _CcnetUser; +struct _CcnetProcessor; +struct CcnetPacketIO; + +#include +#include + +#define SESSION_CONFIG_FILENAME "ccnet.conf" +#define CCNET_USER_ID_START 1000 + +/** + * CcnetClientMode: + * @CCNET_CLIENT_SYNC: Synchronous mode + * @CCNET_CLIENT_ASYNC: Asynchronous mode + * + * #CcnetClient can run in synchronous or asynchronous mode. In + * synchronous mode, every function call to #CcnetClient is blocked + * until the ccnet daemon returns. In asynchronous mode, function + * calls are not blocked, and the user should use the processor + * mechanism to interact with the daemon. + * + **/ +typedef enum { + CCNET_CLIENT_SYNC, + CCNET_CLIENT_ASYNC +} CcnetClientMode; + + + +struct CcnetResponse { + char *code; + char *code_msg; + char *content; + int clen; /* content length */ +}; + +typedef struct CcnetClientPriv CcnetClientPriv; + + +/** + * CcnetClient: + * @id: The ccnet ID in plain text, including the ending '\0' + * @id_sha1: The ccnet ID in binary. + * @id_quark: The ccnet ID in GQuark. + * @name: Ccnet name + * @mode: See #CcnetClientMode + */ +struct _CcnetClient +{ + CcnetSessionBase base; + + /*< public >*/ + + int mode; + + char *central_config_dir; + char *config_dir; + char *config_file; + + int daemon_port; + char *un_path; + + int connected : 1; + + struct _CcnetProcFactory *proc_factory; + struct _CcnetGroupManager *group_mgr; + /* For processor threads. */ + struct _CcnetJobManager *job_mgr; + + struct CcnetResponse response; + + + /*< private >*/ + evutil_socket_t connfd; + uint32_t req_id; /* the current request id */ + + struct CcnetPacketIO *io; + + GHashTable *processors; + GList *rpc_pool; + + CcnetClientPriv *priv; +}; + +struct _CcnetClientClass +{ + CcnetSessionBaseClass parent_class; +}; + +GType ccnet_client_get_type (void); + +CcnetClient* ccnet_client_new (void); +int ccnet_client_load_confdir (CcnetClient *client, const char *central_config_dir, const char *confdir); + +/* +void ccnet_client_add_alias (CcnetClient *client, const char *alias_str); +void ccnet_client_del_alias (CcnetClient *client, const char *alias_str); +gboolean ccnet_client_has_alias (CcnetClient *client, const char *alias_id); +*/ + +int ccnet_client_connect_daemon (CcnetClient *client, CcnetClientMode mode); +int ccnet_client_disconnect_daemon (CcnetClient *client); + + +uint32_t ccnet_client_get_request_id (CcnetClient *client); + +/* async mode */ +void ccnet_client_run_synchronizer (CcnetClient *client); + +void ccnet_client_add_processor (CcnetClient *client, + CcnetProcessor *processor); + +void ccnet_client_remove_processor (CcnetClient *client, + CcnetProcessor *processor); + +CcnetProcessor * + ccnet_client_get_processor (CcnetClient *client, int id); + +int ccnet_client_read_input (CcnetClient *client); + +/* sync mode */ +int ccnet_client_read_response (CcnetClient *client); + +void ccnet_client_send_request (CcnetClient *client, + int req_id, const char *req); + +void ccnet_client_send_update (CcnetClient *client, int req_id, + const char *code, const char *reason, + const char *content, int clen); + +void ccnet_client_send_response (CcnetClient *client, int req_id, + const char *code, const char *reason, + const char *content, int clen); + +const char *ccnet_client_send_cmd (CcnetClient *client, + const char *cmd, GError **error); + +int ccnet_client_send_message (CcnetClient *client, + CcnetMessage *message); + + +/* For a sync client to receive a given type of message, + 1. call ccnet_client_prepare_recv_message() with the message type you want to receive + 2. call ccnet_client_receive_message() repeatly + */ +int +ccnet_client_prepare_recv_message (CcnetClient *client, + const char *app); + +CcnetMessage * +ccnet_client_receive_message (CcnetClient *client); + +uint32_t +ccnet_client_get_rpc_request_id (CcnetClient *client, const char *peer_id, + const char *service); +void +ccnet_client_clean_rpc_request (CcnetClient *client, uint32_t req_id); + +/* void ccnet_client_send_event (CcnetClient *client, GObject *event); */ + +#endif diff --git a/include/ccnet/ccnet-session-base.h b/include/ccnet/ccnet-session-base.h new file mode 100644 index 0000000..4cc6737 --- /dev/null +++ b/include/ccnet/ccnet-session-base.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_SESSION_BASE_H +#define CCNET_SESSION_BASE_H + +#include + +#define CCNET_TYPE_SESSION_BASE (ccnet_session_base_get_type ()) +#define CCNET_SESSION_BASE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_SESSION_BASE, CcnetSessionBase)) +#define CCNET_IS_SESSION_BASE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_SESSION_BASE)) +#define CCNET_SESSION_BASE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_SESSION_BASE, CcnetSessionBaseClass)) +#define CCNET_IS_SESSION_BASE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_SESSION_BASE)) +#define CCNET_SESSION_BASE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_SESSION_BASE, CcnetSessionBaseClass)) + +#define CCNET_PIPE_NAME "ccnet.sock" + +typedef struct _CcnetSessionBase CcnetSessionBase; +typedef struct _CcnetSessionBaseClass CcnetSessionBaseClass; + +struct _CcnetSessionBase { + GObject parent_instance; + + char id[41]; + unsigned char id_sha1[20]; + + char *user_name; + + char *name; + + int public_port; + int net_status; + + char *service_url; + char *relay_id; +}; + + +struct _CcnetSessionBaseClass { + GObjectClass parent_class; +}; + + +GType ccnet_session_base_get_type (void); + +CcnetSessionBase *ccnet_session_base_new (void); + +#endif diff --git a/include/ccnet/ccnetrpc-transport.h b/include/ccnet/ccnetrpc-transport.h new file mode 100644 index 0000000..5c6a696 --- /dev/null +++ b/include/ccnet/ccnetrpc-transport.h @@ -0,0 +1,30 @@ +#ifndef CCNETRPC_TRANPORT_H +#define CCNETRPC_TRANPORT_H + +#include + +typedef struct { + /* either session or pool will be set. */ + CcnetClient *session; + CcnetClientPool *pool; + char *peer_id; /* NULL if local */ + char *service; +} CcnetrpcTransportParam; /* this structure will be parsed to + * ccnet_transport_send () + */ + +typedef struct { + CcnetClient *session; + char *peer_id; /* NULL if local */ + char *service; +} CcnetrpcAsyncTransportParam; /* this structure will be parsed to + * ccnet_async_transport_send () + */ + +char *ccnetrpc_transport_send (void *arg, + const gchar *fcall_str, size_t fcall_len, size_t *ret_len); + +int ccnetrpc_async_transport_send (void *arg, gchar *fcall_str, + size_t fcall_len, void *rpc_priv); + +#endif /* SEARPC_TRANPORT_H */ diff --git a/include/ccnet/cevent.h b/include/ccnet/cevent.h new file mode 100644 index 0000000..25be11c --- /dev/null +++ b/include/ccnet/cevent.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + * CEvent is used for send message from a work thread to main thread. + */ +#ifndef CEVENT_H +#define CEVENT_H + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#include +#include +#else +#include +#endif + +#include + +#include + +#ifdef WIN32 +#define ccnet_pipe_t intptr_t +#else +#define ccnet_pipe_t int +#endif + +typedef struct CEvent CEvent; + +typedef void (*cevent_handler) (CEvent *event, void *handler_data); + +struct CEvent { + uint32_t id; + void *data; +}; + + +typedef struct CEventManager CEventManager; + +struct CEventManager { + + ccnet_pipe_t pipefd[2]; + struct event event; + GHashTable *handler_table; + uint32_t next_id; + + pthread_mutex_t mutex; +}; + +CEventManager* cevent_manager_new (); + +int cevent_manager_start (CEventManager *manager); + +uint32_t cevent_manager_register (CEventManager *manager, + cevent_handler handler, void *handler_data); + +void cevent_manager_unregister (CEventManager *manager, uint32_t id); + +void cevent_manager_add_event (CEventManager *manager, uint32_t id, + void *event_data); + +#endif diff --git a/include/ccnet/invoke-service-proc.h b/include/ccnet/invoke-service-proc.h new file mode 100644 index 0000000..138e91f --- /dev/null +++ b/include/ccnet/invoke-service-proc.h @@ -0,0 +1,39 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_SERVICE_PROC_H +#define CCNET_SERVICE_PROC_H + +#include +#include "processor.h" + +#define CCNET_TYPE_SERVICE_PROC (ccnet_service_proc_get_type ()) +#define CCNET_SERVICE_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_SERVICE_PROC, CcnetServiceProc)) +#define CCNET_IS_SERVICE_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_SERVICE_PROC)) +#define CCNET_SERVICE_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_SERVICE_PROC, CcnetServiceProcClass)) +#define CCNET_IS_SERVICE_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_SERVICE_PROC)) +#define CCNET_SERVICE_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_SERVICE_PROC, CcnetServiceProcClass)) + +typedef struct _CcnetServiceProc CcnetServiceProc; +typedef struct _CcnetServiceProcClass CcnetServiceProcClass; + +typedef void (*ResponseCB) (const char *code, const char *code_msg, + char *content, int clen, void *cb_data); + +struct _CcnetServiceProc { + CcnetProcessor parent_instance; + + ResponseCB resp_cb; + void *cb_data; +}; + +struct _CcnetServiceProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_service_proc_get_type (); + +void ccnet_service_proc_set_response_cb (CcnetServiceProc *proc, + ResponseCB resp_cb, void *data); + + +#endif diff --git a/include/ccnet/job-mgr.h b/include/ccnet/job-mgr.h new file mode 100644 index 0000000..0a779ef --- /dev/null +++ b/include/ccnet/job-mgr.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/** + * Job Manager manages long term jobs. These jobs are run in their + * own threads. + */ + +#ifndef JOB_MGR_H +#define JOB_MGR_H + +#include + +struct _CcnetSession; + +typedef struct _CcnetJob CcnetJob; +typedef struct _CcnetJobManager CcnetJobManager; + +/* + The thread func should return the result back by + return (void *)result; + The result will be passed to JobDoneCallback. + */ +typedef void* (*JobThreadFunc)(void *data); +typedef void (*JobDoneCallback)(void *result); + + +struct _CcnetJobManager { + GHashTable *jobs; + + GThreadPool *thread_pool; + + int next_job_id; +}; + +void +ccnet_job_cancel (CcnetJob *job); + +CcnetJobManager * +ccnet_job_manager_new (int max_threads); + +void +ccnet_job_manager_free (CcnetJobManager *mgr); + +int +ccnet_job_manager_schedule_job (CcnetJobManager *mgr, + JobThreadFunc func, + JobDoneCallback done_func, + void *data); + +/** + * Wait a specific job to be done. + */ +void +ccnet_job_manager_wait_job (CcnetJobManager *mgr, int job_id); + + +#endif diff --git a/include/ccnet/message.h b/include/ccnet/message.h new file mode 100644 index 0000000..982c71c --- /dev/null +++ b/include/ccnet/message.h @@ -0,0 +1,78 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_MESSAGE_H +#define CCNET_MESSAGE_H + + +#include +#include + +#define CCNET_TYPE_MESSAGE (ccnet_message_get_type ()) +#define CCNET_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_MESSAGE, CcnetMessage)) +#define CCNET_IS_MESSAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_MESSAGE)) +#define CCNET_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_MESSAGE, CcnetMessageClass)) +#define CCNET_IS_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_MESSAGE)) +#define CCNET_MESSAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_MESSAGE, CcnetMessageClass)) + +typedef struct _CcnetMessage CcnetMessage; +typedef struct _CcnetMessageClass CcnetMessageClass; + +#define FLAG_TO_GROUP 0x01 +#define FLAG_IS_ERROR 0x02 +#define FLAG_WITH_BLOOM 0x04 +#define FLAG_IS_ACK 0x08 +#define FLAG_IS_RENDEZVOUS 0x10 +#define FLAG_TO_USER 0x20 + +struct _CcnetMessage +{ + GObject parent_instance; + + char flags; + char *id; /* UUID */ + + char from[41]; + char to[41]; + + int ctime; /* creation time */ + int rtime; /* receive time */ + + char *app; /* application */ + char *body; +}; + +struct _CcnetMessageClass +{ + GObjectClass parent_class; +}; + +GType ccnet_message_get_type (void); + +CcnetMessage* ccnet_message_new (const char *from_id, + const char *to_id, + const char *app, + const char *body, + int flags); + +CcnetMessage* ccnet_message_new_full (const char *from_id, + const char *to_id, + const char *app, + const char *body, + time_t ctime, + time_t rcv_time, + const char *msg_id, + int flags); + +void ccnet_message_free (CcnetMessage *msg); + +void ccnet_message_to_string_buf (CcnetMessage *msg, GString *buf); +CcnetMessage *ccnet_message_from_string (char *buf, int len); + +gboolean ccnet_message_is_to_group(CcnetMessage *msg); + +/* to avoid string allocation */ +/* inline void ccnet_message_body_take (CcnetMessage *msg, char *body); */ + +/* inline void ccnet_message_body_dup (CcnetMessage *msg, char *body); */ + +#endif diff --git a/include/ccnet/mqclient-proc.h b/include/ccnet/mqclient-proc.h new file mode 100644 index 0000000..730a3c7 --- /dev/null +++ b/include/ccnet/mqclient-proc.h @@ -0,0 +1,44 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_MQCLIENT_PROC_H +#define CCNET_MQCLIENT_PROC_H + +#include + +#include "processor.h" +#include "message.h" + +#define CCNET_TYPE_MQCLIENT_PROC (ccnet_mqclient_proc_get_type ()) +#define CCNET_MQCLIENT_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_MQCLIENT_PROC, CcnetMqclientProc)) +#define CCNET_IS_MQCLIENT_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_MQCLIENT_PROC)) +#define CCNET_MQCLIENT_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_MQCLIENT_PROC, CcnetMqclientProcClass)) +#define CCNET_IS_MQCLIENT_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_MQCLIENT_PROC)) +#define CCNET_MQCLIENT_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_MQCLIENT_PROC, CcnetMqclientProcClass)) + +typedef struct _CcnetMqclientProc CcnetMqclientProc; +typedef struct _CcnetMqclientProcClass CcnetMqclientProcClass; + +typedef void (*MessageGotCB) (CcnetMessage *message, void *data); + +struct _CcnetMqclientProc { + CcnetProcessor parent_instance; + + MessageGotCB message_got_cb; + void *cb_data; +}; + +struct _CcnetMqclientProcClass { + CcnetProcessorClass parent_class; +}; + +void ccnet_mqclient_proc_set_message_got_cb (CcnetMqclientProc *, + MessageGotCB, void *); + +GType ccnet_mqclient_proc_get_type (); + +void ccnet_mqclient_proc_put_message (CcnetMqclientProc *proc, + CcnetMessage *message); + +void ccnet_mqclient_proc_unsubscribe_apps (CcnetMqclientProc *proc); + +#endif diff --git a/include/ccnet/option.h b/include/ccnet/option.h new file mode 100644 index 0000000..02efdd6 --- /dev/null +++ b/include/ccnet/option.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_OPTION_H +#define CCNET_OPTION_H + +#include +#include + +#ifdef WIN32 +static inline char *GetDeafaultDir() +{ + static char buf[128]; + static int inited = 0; + + if (!inited) { + const char *home = g_get_home_dir(); + inited = 1; + snprintf(buf, 128, "%s/ccnet", home); + } + return buf; +} + + #define DEFAULT_CONFIG_DIR GetDeafaultDir() + #define CONFIG_FILE_NAME "ccnet.conf" + #define PREFS_FILE_NAME "prefs.conf" +#else + #define DEFAULT_CONFIG_DIR "~/.ccnet" + #define CONFIG_FILE_NAME "ccnet.conf" + #define PREFS_FILE_NAME "prefs.conf" +#endif + +#define PEER_KEYFILE "mykey.peer" + +#define MAX_USERNAME_LEN 20 +#define MIN_USERNAME_LEN 2 + +#define DEFAULT_PORT 10001 + +#define CHAT_APP "Chat" +#define PEERMGR_APP "PeerMgr" +#define GROUPMGR_APP "GroupMgr" + + +enum { + NET_STATUS_DOWN, + NET_STATUS_INNAT, + NET_STATUS_FULL +}; + +#endif diff --git a/include/ccnet/packet.h b/include/ccnet/packet.h new file mode 100644 index 0000000..e110ba6 --- /dev/null +++ b/include/ccnet/packet.h @@ -0,0 +1,34 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_PEER_MSG +#define CCNET_PEER_MSG + +#define CCNET_MSG_OK 0 +#define CCNET_MSG_HANDSHAKE 1 +#define CCNET_MSG_REQUEST 2 +#define CCNET_MSG_RESPONSE 3 +#define CCNET_MSG_UPDATE 4 +#define CCNET_MSG_RELAY 5 /* NOT USED NOW */ +#define CCNET_MSG_ENCPACKET 6 /* an encrypt packet */ + +typedef struct ccnet_header ccnet_header; + +struct ccnet_header { + uint8_t version; + uint8_t type; + uint16_t length; /* length of payload */ + uint32_t id; /* used as length in ecrypted packet */ +}; + +typedef struct ccnet_packet ccnet_packet; + +struct ccnet_packet { + struct ccnet_header header; + char data[0]; +}; + +#define CCNET_PACKET_MAX_PAYLOAD_LEN 65535 +#define CCNET_PACKET_LENGTH_HEADER 8 +#define CCNET_USER_ID_START 1000 + +#endif diff --git a/include/ccnet/peer.h b/include/ccnet/peer.h new file mode 100644 index 0000000..ddfceab --- /dev/null +++ b/include/ccnet/peer.h @@ -0,0 +1,88 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_PEER_H +#define CCNET_PEER_H + +#include +#include +#include + + +#define CCNET_TYPE_PEER (ccnet_peer_get_type ()) +#define CCNET_PEER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_PEER, CcnetPeer)) +#define CCNET_IS_PEER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_PEER)) +#define CCNET_PEER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_PEER, CcnetPeerClass)) +#define CCNET_IS_PEER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_PEER)) +#define CCNET_PEER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_PEER, CcnetPeerClass)) + + +enum { + PEER_DOWN, + PEER_CONNECTED +}; + +enum { + BIND_UNKNOWN, + BIND_YES, + BIND_NO +}; + +typedef struct _CcnetPeer CcnetPeer; +typedef struct _CcnetPeerClass CcnetPeerClass; + +#define CCNET_PEERID_LEN 40 + + +struct _CcnetPeer +{ + GObject parent_instance; + char id[41]; + char user_id[41]; + + gint64 timestamp; + + char *name; + + unsigned int is_self : 1; + unsigned int can_connect : 1; + unsigned int in_local_network : 1; + unsigned int in_connection : 1; + unsigned int is_ready : 1; + unsigned int encrypt_channel : 1; + + gboolean login_started; + char *login_error; + gboolean logout_started; + + + char *public_addr; + uint16_t public_port; + char *service_url; /* http server for relay in seaflie */ + + char *addr_str; + uint16_t port; + + int net_state; + + GList *role_list; + + GList *myrole_list; /* my role on this user */ + + gint8 bind_status; + char *bind_email; + char *session_key; +}; + +struct _CcnetPeerClass +{ + GObjectClass parent_class; +}; + + +GType ccnet_peer_get_type (void); + + +CcnetPeer* ccnet_peer_new (const char *id); + + +#endif diff --git a/include/ccnet/proc-factory.h b/include/ccnet/proc-factory.h new file mode 100644 index 0000000..211613a --- /dev/null +++ b/include/ccnet/proc-factory.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_PROC_FACTORY_H +#define CCNET_PROC_FACTORY_H + + +#include + +#include "processor.h" + + +#define CCNET_TYPE_PROC_FACTORY (ccnet_proc_factory_get_type ()) +#define CCNET_PROC_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_PROC_FACTORY, CcnetProcFactory)) +#define CCNET_IS_PROC_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_PROC_FACTORY)) +#define CCNET_PROC_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_PROC_FACTORY, CcnetProcFactoryClass)) +#define CCNET_IS_PROC_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_PROC_FACTORY)) +#define CCNET_PROC_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_PROC_FACTORY, CcnetProcFactoryClass)) + +typedef struct _CcnetProcFactory CcnetProcFactory; +typedef struct _CcnetProcFactoryClass CcnetProcFactoryClass; + +struct _CcnetProcFactory +{ + GObject parent_instance; + + /* protected */ + CcnetClient *session; +}; + +struct _CcnetProcFactoryClass +{ + GObjectClass parent_class; +}; + +GType ccnet_proc_factory_get_type (void); + +CcnetProcFactory *ccnet_proc_factory_new (CcnetClient *session); + +void ccnet_proc_factory_register_processor (CcnetProcFactory *facotry, + const char *serv_name, + GType proc_type); + + +enum { + SLAVE = 0, + MASTER = 1, +}; + + +CcnetProcessor* +ccnet_proc_factory_create_processor (CcnetProcFactory *factory, + const char *serv_name, + int is_master, + int req_id); + +CcnetProcessor* +ccnet_proc_factory_create_master_processor (CcnetProcFactory *factory, + const char *serv_name); + +CcnetProcessor * +ccnet_proc_factory_create_remote_master_processor (CcnetProcFactory *factory, + const char *serv_name, + const char *peer_id); +CcnetProcessor * +ccnet_proc_factory_create_slave_processor (CcnetProcFactory *factory, + const char *serv_name, + const char *peer_id, + int req_id); + +void ccnet_proc_factory_recycle(CcnetProcFactory *factory, + CcnetProcessor *processor); + + +#endif diff --git a/include/ccnet/processor.h b/include/ccnet/processor.h new file mode 100644 index 0000000..76cc748 --- /dev/null +++ b/include/ccnet/processor.h @@ -0,0 +1,168 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_PROCESSOR_H +#define CCNET_PROCESSOR_H + +#include +#include +#include +#include + +#include + +#ifdef WIN32 +#define ccnet_pipe_t intptr_t +#else +#define ccnet_pipe_t int +#endif + +struct _CcnetClient; + +#define CCNET_TYPE_PROCESSOR (ccnet_processor_get_type ()) +#define CCNET_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_PROCESSOR, CcnetProcessor)) +#define CCNET_IS_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_PROCESSOR)) +#define CCNET_PROCESSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_PROCESSOR, CcnetProcessorClass)) +#define CCNET_IS_PROCESSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_PROCESSOR)) +#define CCNET_PROCESSOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_PROCESSOR, CcnetProcessorClass)) + +typedef struct _CcnetProcessor CcnetProcessor; +typedef struct _CcnetProcessorClass CcnetProcessorClass; + + +struct _CcnetProcessor { + GObject parent_instance; + + char *peer_id; + struct _CcnetClient *session; + + char *name; + + /* highest bit = 0, master; highest bit = 1, slave */ + uint32_t id; + int state; + int failure; + + struct CcnetTimer *timer; + + int num_retry; + + /* Set to 1 when handling update or response */ + gboolean is_active; + + /* Internal flags for handling processor thread. */ + gboolean thread_running; + gboolean delay_shutdown; + gboolean was_success; +}; + +enum { + STATE_IN_SHUTDOWN = 1 << 8, + STATE_RECYCLED, +}; + +enum { + PROC_NOTSET, + PROC_DONE, + PROC_REMOTE_DEAD, + PROC_NO_SERVICE, + PROC_PERM_ERR, + PROC_BAD_RESP, /* code[0] =='5' || '4' */ +}; + +#define SLAVE_MASK 0x80000000 +#define REQUEST_ID_MASK 0x7fffffff +#define REQUEST_ID(processor_id) ((processor_id) & REQUEST_ID_MASK) +#define UPDATE_ID(processor_id) ((processor_id) & REQUEST_ID_MASK) +#define RESPONSE_ID(processor_id) ((processor_id) & REQUEST_ID_MASK) +#define SLAVE_ID(request_id) ((request_id) | SLAVE_MASK) +#define MASTER_ID(request_id) (request_id) + +#define PRINT_ID(processor_id) ((processor_id) & SLAVE_MASK) ? \ + - REQUEST_ID(processor_id) : REQUEST_ID(processor_id) +#define IS_SLAVE(processor) ((processor)->id & SLAVE_MASK) +#define GET_PNAME(processor) CCNET_PROCESSOR_GET_CLASS(processor)->name + +struct _CcnetProcessorClass { + GObjectClass parent_class; + + char *name; + + /* pure virtual function */ + int (*start) (CcnetProcessor *processor, + int argc, char **argv); + void (*handle_update) (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + void (*handle_response) (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + + void (*handle_sigchld) (CcnetProcessor *processor, + int status); + + void (*shutdown) (CcnetProcessor *processor); + + void (*release_resource) (CcnetProcessor *processor); +}; + +GType ccnet_processor_get_type (); + +int ccnet_processor_start (CcnetProcessor *processor, + int argc, char **argv); + +int ccnet_processor_startl + (CcnetProcessor *processor, ...) G_GNUC_NULL_TERMINATED; + +void ccnet_processor_done (CcnetProcessor *processor, gboolean success); + +void ccnet_processor_handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +void ccnet_processor_handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +void ccnet_processor_handle_sigchld (CcnetProcessor *processor, + int status); + + +void ccnet_processor_send_request (CcnetProcessor *processor, + const char *request); + +void ccnet_processor_send_request_l (CcnetProcessor *processor, + ...) G_GNUC_NULL_TERMINATED; + +void ccnet_processor_send_update(CcnetProcessor *processor, + const char *code, + const char *code_msg, + const char *content, int clen); + +void ccnet_processor_send_response(CcnetProcessor *processor, + const char *code, + const char *code_msg, + const char *content, int clen); + +/* + The thread func should return the result back by + return (void *)result; + The result will be passed to ProcThreadDoneFunc. + In the done func, the caller should check whether processor->delay_shutdown + is TRUE. If it is, you should call processor_done(). + */ +typedef void* (*ProcThreadFunc)(void *data); +typedef void (*ProcThreadDoneFunc)(void *result); + +struct _CcnetJobManager; + +/* + * @job_mgr: the thread pool to create the worker thread. + * If it's NULL, processor->session->job_mgr will be used. + */ +int ccnet_processor_thread_create (CcnetProcessor *processor, + struct _CcnetJobManager *job_mgr, + ProcThreadFunc func, + ProcThreadDoneFunc done_func, + void *data); + +#endif diff --git a/include/ccnet/rpcserver-proc.h b/include/ccnet/rpcserver-proc.h new file mode 100644 index 0000000..555c0fd --- /dev/null +++ b/include/ccnet/rpcserver-proc.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_RPCSERVER_PROC_H +#define CCNET_RPCSERVER_PROC_H + +#include + + +#define CCNET_TYPE_RPCSERVER_PROC (ccnet_rpcserver_proc_get_type ()) +#define CCNET_RPCSERVER_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_RPCSERVER_PROC, CcnetRpcserverProc)) +#define CCNET_IS_RPCSERVER_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_RPCSERVER_PROC)) +#define CCNET_RPCSERVER_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_RPCSERVER_PROC, CcnetRpcserverProcClass)) +#define IS_CCNET_RPCSERVER_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_RPCSERVER_PROC)) +#define CCNET_RPCSERVER_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_RPCSERVER_PROC, CcnetRpcserverProcClass)) + +typedef struct _CcnetRpcserverProc CcnetRpcserverProc; +typedef struct _CcnetRpcserverProcClass CcnetRpcserverProcClass; + +struct _CcnetRpcserverProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetRpcserverProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_rpcserver_proc_get_type (); + +#endif + diff --git a/include/ccnet/sendcmd-proc.h b/include/ccnet/sendcmd-proc.h new file mode 100644 index 0000000..fb30e26 --- /dev/null +++ b/include/ccnet/sendcmd-proc.h @@ -0,0 +1,41 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_CMD_PROC_H +#define CCNET_CMD_PROC_H + +#include + +#include "processor.h" + +#define CCNET_TYPE_SENDCMD_PROC (ccnet_sendcmd_proc_get_type ()) +#define CCNET_SENDCMD_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_SENDCMD_PROC, CcnetSendcmdProc)) +#define CCNET_IS_SENDCMD_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_SENDCMD_PROC)) +#define CCNET_SENDCMD_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_SENDCMD_PROC, CcnetSendcmdProcClass)) +#define CCNET_IS_SENDCMD_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_SENDCMD_PROC)) +#define CCNET_SENDCMD_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_SENDCMD_PROC, CcnetSendcmdProcClass)) + +typedef struct _CcnetSendcmdProc CcnetSendcmdProc; +typedef struct _CcnetSendcmdProcClass CcnetSendcmdProcClass; + +typedef int (*SendcmdProcRcvrspCallback) (const char *code, char *content, + int clen, void *data); + +struct _CcnetSendcmdProc { + CcnetProcessor parent_instance; + + SendcmdProcRcvrspCallback rcvrsp_cb; + void *cb_data; +}; + +struct _CcnetSendcmdProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_sendcmd_proc_get_type (); + +int ccnet_sendcmd_proc_send_command (CcnetSendcmdProc *proc, const char *cmd); +void ccnet_sendcmd_proc_set_rcvrsp_cb (CcnetSendcmdProc *proc, + SendcmdProcRcvrspCallback rcvrsp_cb, + void *data); + +#endif diff --git a/include/ccnet/status-code.h b/include/ccnet/status-code.h new file mode 100644 index 0000000..8c77e72 --- /dev/null +++ b/include/ccnet/status-code.h @@ -0,0 +1,64 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef STATUS_CODE_H +#define STATUS_CODE_H + +/* SC: Status Code. SS: Status String */ + +/* Error Code for use in GError */ +#define EC_NETWORK_ERR 1 +#define ES_NETWORK_ERR "Network Error" + + +#define SC_PROC_KEEPALIVE "100" +#define SS_PROC_KEEPALIVE "processor keep alive" +#define SC_PROC_ALIVE "101" +#define SS_PROC_ALIVE "processor is alive" +#define SC_PROC_DEAD "102" +#define SS_PROC_DEAD "processor is dead" +#define SC_PROC_DONE "103" +#define SS_PROC_DONE "service is done" + + +#define SC_OK "200" +#define SS_OK "OK" +#define SC_PERM_CHECKING "250" +#define SS_PERM_CHECKING "Permission Checking" + + +#define SC_SHUTDOWN "500" +#define SS_SHUTDOWN "Shutdown" +#define SC_CREATE_PROC_ERR "501" +#define SS_CREATE_PROC_ERR "Create Processor Error" +#define SC_BAD_PEER "502" +#define SS_BAD_PEER "Bad peer id" +#define SC_BAD_USER "502" +#define SS_BAD_USER "Bad user id" +#define SC_BAD_ARGS "503" +#define SS_BAD_ARGS "Bad arguments" +#define SC_PERM_ERR "504" +#define SS_PERM_ERR "Permission Error" +#define SC_BAD_UPDATE_CODE "506" +#define SS_BAD_UPDATE_CODE "Bad update code" +#define SC_BAD_RESPONSE_CODE "507" +#define SS_BAD_RESPONSE_CODE "Bad response code" +#define SC_VERSION_MISMATCH "508" +#define SS_VERSION_MISMATCH "Version Mismatch" +#define SC_UNKNOWN_PEER "510" +#define SS_UNKNOWN_PEER "Unknown peer" +#define SC_UNKNOWN_SERVICE "511" +#define SS_UNKNOWN_SERVICE "Unknown service" +#define SC_UNKNOWN_SERVICE_IN_PERM "511" +#define SS_UNKNOWN_SERVICE_IN_PERM "Unknown service in permission check" +#define SC_PEER_UNREACHABLE "512" +#define SS_PEER_UNREACHABLE "Peer Unreachable" +#define SC_CON_TIMEOUT "513" +#define SS_CON_TIMEOUT "connection timeout" +#define SC_KEEPALIVE_TIMEOUT "514" +#define SS_KEEPALIVE_TIMEOUT "keepalive timeout" +#define SC_NETDOWN "515" +#define SS_NETDOWN "peer down" +#define SC_SERV_EXISTED "516" +#define SS_SERV_EXISTED "The service existed" + +#endif diff --git a/include/ccnet/threaded-rpcserver-proc.h b/include/ccnet/threaded-rpcserver-proc.h new file mode 100644 index 0000000..4894fdb --- /dev/null +++ b/include/ccnet/threaded-rpcserver-proc.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_THREADED_RPCSERVER_PROC_H +#define CCNET_THREADED_RPCSERVER_PROC_H + +#include + + +#define CCNET_TYPE_THREADED_RPCSERVER_PROC (ccnet_threaded_rpcserver_proc_get_type ()) +#define CCNET_THREADED_RPCSERVER_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_THREADED_RPCSERVER_PROC, CcnetThreadedRpcserverProc)) +#define CCNET_IS_THREADED_RPCSERVER_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_THREADED_RPCSERVER_PROC)) +#define CCNET_THREADED_RPCSERVER_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_THREADED_RPCSERVER_PROC, CcnetThreadedRpcserverProcClass)) +#define IS_CCNET_THREADED_RPCSERVER_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_THREADED_RPCSERVER_PROC)) +#define CCNET_THREADED_RPCSERVER_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_THREADED_RPCSERVER_PROC, CcnetThreadedRpcserverProcClass)) + +typedef struct _CcnetThreadedRpcserverProc CcnetThreadedRpcserverProc; +typedef struct _CcnetThreadedRpcserverProcClass CcnetThreadedRpcserverProcClass; + +struct _CcnetThreadedRpcserverProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetThreadedRpcserverProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_threaded_rpcserver_proc_get_type (); + +#endif + diff --git a/include/ccnet/timer.h b/include/ccnet/timer.h new file mode 100644 index 0000000..eafe312 --- /dev/null +++ b/include/ccnet/timer.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_TIMER_H +#define CCNET_TIMER_H + +/* return TRUE to reschedule the timer, return FALSE to cancle the timer */ +typedef int (*TimerCB) (void *data); + +struct CcnetTimer; + +typedef struct CcnetTimer CcnetTimer; + +/** + * Calls timer_func(user_data) after the specified interval. + * The timer is freed if timer_func returns zero. + * Otherwise, it's called again after the same interval. + */ +CcnetTimer* ccnet_timer_new (TimerCB func, + void *user_data, + uint64_t timeout_milliseconds); + +/** + * Frees a timer and sets the timer pointer to NULL. + */ +void ccnet_timer_free (CcnetTimer **timer); + + +#endif diff --git a/include/ccnet/valid-check.h b/include/ccnet/valid-check.h new file mode 100644 index 0000000..9b746da --- /dev/null +++ b/include/ccnet/valid-check.h @@ -0,0 +1,29 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef VALID_CHECK_H +#define VALID_CHECK_H + +#include + +inline static gboolean user_id_valid(const char *id) +{ + if (!id || strlen(id) != 40) + return FALSE; + return TRUE; +} + +inline static gboolean peer_id_valid(const char *id) +{ + if (!id || strlen(id) != 40) + return FALSE; + return TRUE; +} + +gboolean is_uuid_valid (const char *uuid_str); + +inline static gboolean group_id_valid(const char *id) +{ + return is_uuid_valid(id); +} + +#endif diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..99720b8 --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,102 @@ + +AM_CPPFLAGS = @GLIB2_CFLAGS@ -I$(top_srcdir)/include \ + -I$(top_srcdir)/include/ccnet \ + -I$(top_srcdir)/lib \ + -I$(top_builddir)/include \ + -DG_LOG_DOMAIN=\"Ccnet\" \ + @SEARPC_CFLAGS@ \ + @MSVC_CFLAGS@ \ + -Wall + +BUILT_SOURCES = gensource + +lib_LTLIBRARIES = libccnet.la + +noinst_HEADERS = buffer.h \ + packet-io.h \ + include.h \ + marshal.h \ + peer-common.h \ + string-util.h \ + libccnet_utils.h \ + ccnet-object.h \ + rpc-common.h \ + net.h \ + utils.h \ + bloom-filter.h \ + db.h \ + rsa.h + +ccnetincludedir = $(includedir)/ccnet +ccnetinclude_DATA = ccnet-object.h + +libccnet_la_CPPFLAGS = $(AM_CPPFLAGS) -DCCNET_LIB @PTHREAD_CFLAGS@ + +libccnet_la_SOURCES = ccnet-client.c packet-io.c libccnet_utils.c \ + message.c proc-factory.c \ + processor.c buffer.c \ + peer.c sendcmd-proc.c \ + mqclient-proc.c invoke-service-proc.c \ + marshal.c \ + mainloop.c cevent.c timer.c ccnet-session-base.c job-mgr.c \ + rpcserver-proc.c ccnetrpc-transport.c threaded-rpcserver-proc.c \ + ccnetobj.c \ + async-rpc-proc.c ccnet-rpc-wrapper.c \ + client-pool.c + +EXTRA_DIST = ccnetobj.vala rpc_table.py + +libccnet_la_LDFLAGS = -no-undefined -version-info 0:0:0 +libccnet_la_LIBADD = @PTHREAD_LIBS@ @GLIB2_LIBS@ @GOBJECT_LIBS@ @SSL_LIBS@ @LIB_GDI32@ \ + @LIB_UUID@ @LIB_WS32@ @LIB_PSAPI@ -lsqlite3 \ + @LIBEVENT_LIBS@ @SEARPC_LIBS@ @LIB_SHELL32@ + + +noinst_LTLIBRARIES = libccnetd.la + +libccnetd_la_SOURCES = utils.c db.c job-mgr.c \ + rsa.c bloom-filter.c marshal.c net.c timer.c ccnet-session-base.c \ + ccnetobj.c + +libccnetd_la_LDFLAGS = -no-undefined +libccnetd_la_LIBADD = @GLIB2_LIBS@ @GOBJECT_LIBS@ @SSL_LIBS@ @LIB_GDI32@ \ + -lsqlite3 @LIBEVENT_LIBS@ @LIB_WS32@ @LIB_UUID@ \ + @LIB_SHELL32@ @LIB_PSAPI@ @SEARPC_LIBS@ + + +ccnet_object_define = ccnetobj.vala + +valac_gen = ccnetobj.c ccnet-object.h + +ccnet-client.c: ccnet-object.h + +ccnet-object.h: ${ccnet_object_define} + rm -f $@ + valac --pkg posix ${ccnet_object_define} -C -H ccnet-object.h + +ccnetobj.c: ${ccnet_object_define} + rm -f $@ + valac -C --pkg posix ${ccnet_object_define} + +searpc_gen = searpc-signature.h searpc-marshal.h + +gensource: ${searpc_gen} ${valac_gen} + +rpc_table.stamp: ${top_srcdir}/lib/rpc_table.py + @rm -f rpc_table.tmp + @touch rpc_table.tmp + @echo "[libsearpc]: generating rpc header files" + @PYTHON@ `which searpc-codegen.py` ${top_srcdir}/lib/rpc_table.py + @echo "[libsearpc]: done" + @mv -f rpc_table.tmp $@ + +${searpc_gen}: rpc_table.stamp + +clean-local: + rm -f ${searpc_gen} + rm -f rpc_table.pyc + rm -f rpc_table.stamp + rm -f rpc_table.tmp + rm -f ${valac_gen} + +CLEANFILES = ${searpc_gen} ${valac_gen} diff --git a/lib/async-rpc-proc.c b/lib/async-rpc-proc.c new file mode 100644 index 0000000..b67a4ec --- /dev/null +++ b/lib/async-rpc-proc.c @@ -0,0 +1,137 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include + +#include +#include +#include "rpc-common.h" +#include + +#define MAX_RET_LEN 5242880 /* 5M */ + +typedef struct { + const char *service; + char *fcall_str; + size_t fcall_len; + void *rpc_priv; + GString *buf; +} CcnetAsyncRpcProcPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_ASYNC_RPC_PROC, CcnetAsyncRpcProcPriv)) + +G_DEFINE_TYPE (CcnetAsyncRpcProc, ccnet_async_rpc_proc, CCNET_TYPE_PROCESSOR) + +static int start (CcnetProcessor *processor, int argc, char **argv); +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void +release_resource(CcnetProcessor *processor) +{ + CcnetAsyncRpcProcPriv *priv = GET_PRIV (processor); + g_free (priv->fcall_str); + + CCNET_PROCESSOR_CLASS (ccnet_async_rpc_proc_parent_class)->release_resource (processor); +} + + +static void +ccnet_async_rpc_proc_class_init (CcnetAsyncRpcProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->start = start; + proc_class->handle_response = handle_response; + proc_class->release_resource = release_resource; + proc_class->name = "async-rpc-proc"; + + g_type_class_add_private (klass, sizeof(CcnetAsyncRpcProcPriv)); +} + +static void +ccnet_async_rpc_proc_init (CcnetAsyncRpcProc *processor) +{ +} + +void +ccnet_async_rpc_proc_set_rpc (CcnetAsyncRpcProc *proc, + const char *service, + char *fcall_str, + size_t fcall_len, + void *rpc_priv) +{ + CcnetAsyncRpcProcPriv *priv = GET_PRIV (proc); + + priv->service = service; + priv->fcall_str = fcall_str; + priv->fcall_len = fcall_len; + priv->rpc_priv = rpc_priv; +} + + +static int +start (CcnetProcessor *processor, int argc, char **argv) +{ + CcnetAsyncRpcProcPriv *priv = GET_PRIV (processor); + char buf[256]; + + if (argc != 0) { + ccnet_processor_done (processor, FALSE); + return -1; + } + + if (processor->peer_id) { + snprintf (buf, sizeof(buf), "remote %s %s", + processor->peer_id, priv->service); + } else + snprintf (buf, sizeof(buf), "%s", priv->service); + ccnet_processor_send_request (processor, buf); + + return 0; +} + +static void +handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + CcnetAsyncRpcProcPriv *priv = GET_PRIV (processor); + + if (memcmp (code, SC_OK, 3) == 0) { + ccnet_processor_send_update (processor, SC_CLIENT_CALL, SS_CLIENT_CALL, + priv->fcall_str, + priv->fcall_len); + return; + } + + if (memcmp (code, SC_SERVER_RET, 3) == 0) { + if (priv->buf == NULL) + searpc_client_generic_callback (content, clen, priv->rpc_priv, NULL); + else { + g_string_append_len (priv->buf, content, clen); + searpc_client_generic_callback (priv->buf->str, priv->buf->len, + priv->rpc_priv, NULL); + g_string_free (priv->buf, TRUE); + priv->buf = NULL; + } + ccnet_processor_done (processor, TRUE); + } else if (memcmp (code, SC_SERVER_MORE, 3) == 0) { + if (priv->buf == NULL) + priv->buf = g_string_new (NULL); + g_string_append_len (priv->buf, content, clen); + + if (priv->buf->len > MAX_RET_LEN) { + g_warning ("[async-rpc] ret is too long\n"); + g_string_free (priv->buf, TRUE); + priv->buf = NULL; + ccnet_processor_send_update (processor, "400", + "Too many data", NULL, 0); + ccnet_processor_done (processor, FALSE); + } else + ccnet_processor_send_update ( + processor, SC_CLIENT_MORE, SS_CLIENT_MORE, NULL, 0); + } + +} diff --git a/lib/bloom-filter.c b/lib/bloom-filter.c new file mode 100644 index 0000000..f2e9cb2 --- /dev/null +++ b/lib/bloom-filter.c @@ -0,0 +1,176 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include +#include +#include +#include +#include + +#include "bloom-filter.h" + +#define SETBIT(a, n) (a[n/CHAR_BIT] |= (1<<(n%CHAR_BIT))) +#define CLEARBIT(a, n) (a[n/CHAR_BIT] &= ~(1<<(n%CHAR_BIT))) +#define GETBIT(a, n) (a[n/CHAR_BIT] & (1<<(n%CHAR_BIT))) + +Bloom* bloom_create(size_t size, int k, int counting) +{ + Bloom *bloom; + size_t csize = 0; + + if (k <=0 || k > 10) return NULL; + + if ( !(bloom = malloc(sizeof(Bloom))) ) return NULL; + if ( !(bloom->a = calloc((size+CHAR_BIT-1)/CHAR_BIT, sizeof(char))) ) + { + free (bloom); + return NULL; + } + if (counting) { + csize = size*4; + bloom->counters = calloc((csize+CHAR_BIT-1)/CHAR_BIT, sizeof(char)); + if (!bloom->counters) { + free (bloom); + return NULL; + } + } + + bloom->asize = size; + bloom->csize = csize; + bloom->k = k; + bloom->counting = counting; + + return bloom; +} + +int bloom_destroy(Bloom *bloom) +{ + free (bloom->a); + if (bloom->counting) free (bloom->counters); + free (bloom); + + return 0; +} + +static void +incr_bit (Bloom *bf, unsigned int bit_idx) +{ + unsigned int char_idx, offset; + unsigned char value; + unsigned int high; + unsigned int low; + + SETBIT (bf->a, bit_idx); + + if (!bf->counting) return; + + char_idx = bit_idx / 2; + offset = bit_idx % 2; + + value = bf->counters[char_idx]; + low = value & 0xF; + high = (value & 0xF0) >> 4; + + if (offset == 0) { + if (low < 0xF) + low++; + } else { + if (high < 0xF) + high++; + } + value = ((high << 4) | low); + + bf->counters[char_idx] = value; +} + +static void +decr_bit (Bloom *bf, unsigned int bit_idx) +{ + unsigned int char_idx, offset; + unsigned char value; + unsigned int high; + unsigned int low; + + if (!bf->counting) { + CLEARBIT (bf->a, bit_idx); + return; + } + + char_idx = bit_idx / 2; + offset = bit_idx % 2; + + value = bf->counters[char_idx]; + low = value & 0xF; + high = (value & 0xF0) >> 4; + + /* decrement, but once we have reached the max, never go back! */ + if (offset == 0) { + if ((low > 0) && (low < 0xF)) + low--; + if (low == 0) { + CLEARBIT (bf->a, bit_idx); + } + } else { + if ((high > 0) && (high < 0xF)) + high--; + if (high == 0) { + CLEARBIT (bf->a, bit_idx); + } + } + value = ((high << 4) | low); + + bf->counters[char_idx] = value; +} + +int bloom_add(Bloom *bloom, const char *s) +{ + int i; + SHA_CTX c; + unsigned char sha1[20]; + int16_t *sha_int = (int16_t *)&sha1; + + SHA1_Init(&c); + SHA1_Update(&c, s, strlen(s)); + SHA1_Final (sha1, &c); + + for (i=0; i < bloom->k; ++i) + incr_bit (bloom, sha_int[i] % bloom->asize); + + return 0; +} + +int bloom_remove(Bloom *bloom, const char *s) +{ + int i; + SHA_CTX c; + unsigned char sha1[20]; + int16_t *sha_int = (int16_t *)&sha1; + + if (!bloom->counting) + return -1; + + SHA1_Init(&c); + SHA1_Update(&c, s, strlen(s)); + SHA1_Final (sha1, &c); + + for (i=0; i < bloom->k; ++i) + decr_bit (bloom, sha_int[i] % bloom->asize); + + return 0; +} + +int bloom_test(Bloom *bloom, const char *s) +{ + int i; + SHA_CTX c; + unsigned char sha1[20]; + int16_t *sha_int = (int16_t *)&sha1; + + SHA1_Init(&c); + SHA1_Update(&c, s, strlen(s)); + SHA1_Final (sha1, &c); + + for (i=0; i < bloom->k; ++i) + if(!(GETBIT(bloom->a, sha_int[i] % bloom->asize))) return 0; + + return 1; +} diff --git a/lib/bloom-filter.h b/lib/bloom-filter.h new file mode 100644 index 0000000..0c5bd1b --- /dev/null +++ b/lib/bloom-filter.h @@ -0,0 +1,23 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef __BLOOM_H__ +#define __BLOOM_H__ + +#include + +typedef struct { + size_t asize; + unsigned char *a; + size_t csize; + unsigned char *counters; + int k; + char counting:1; +} Bloom; + +Bloom *bloom_create (size_t size, int k, int counting); +int bloom_destroy (Bloom *bloom); +int bloom_add (Bloom *bloom, const char *s); +int bloom_remove (Bloom *bloom, const char *s); +int bloom_test (Bloom *bloom, const char *s); + +#endif diff --git a/lib/buffer.c b/lib/buffer.c new file mode 100644 index 0000000..cb68a95 --- /dev/null +++ b/lib/buffer.c @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2002, 2003 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef WIN32 +#include +#include +#endif + +#ifdef HAVE_VASPRINTF +/* If we have vasprintf, we need to define this before we include stdio.h. */ +#define _GNU_SOURCE +#endif + +#include + +#ifdef HAVE_SYS_TIME_H +#include +#endif + +#ifdef HAVE_SYS_IOCTL_H +#include +#endif + +#include +#include +#include +#include +#include +#ifdef HAVE_STDARG_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "buffer.h" + +struct buffer * +buffer_new(void) +{ + struct buffer *buffer; + + buffer = calloc(1, sizeof(struct buffer)); + + return (buffer); +} + +void +buffer_free(struct buffer *buffer) +{ + if (buffer->orig_buffer != NULL) + free(buffer->orig_buffer); + free(buffer); +} + +/* + * This is a destructive add. The data from one buffer moves into + * the other buffer. + */ + +#define SWAP(x,y) do { \ + (x)->buffer = (y)->buffer; \ + (x)->orig_buffer = (y)->orig_buffer; \ + (x)->misalign = (y)->misalign; \ + (x)->totallen = (y)->totallen; \ + (x)->off = (y)->off; \ +} while (0) + +int +buffer_add_buffer(struct buffer *outbuf, struct buffer *inbuf) +{ + int res; + + /* Short cut for better performance */ + if (outbuf->off == 0) { + struct buffer tmp; + size_t oldoff = inbuf->off; + + /* Swap them directly */ + SWAP(&tmp, outbuf); + SWAP(outbuf, inbuf); + SWAP(inbuf, &tmp); + + /* + * Optimization comes with a price; we need to notify the + * buffer if necessary of the changes. oldoff is the amount + * of data that we transfered from inbuf to outbuf + */ + if (inbuf->off != oldoff && inbuf->cb != NULL) + (*inbuf->cb)(inbuf, oldoff, inbuf->off, inbuf->cbarg); + if (oldoff && outbuf->cb != NULL) + (*outbuf->cb)(outbuf, 0, oldoff, outbuf->cbarg); + + return (0); + } + + res = buffer_add(outbuf, inbuf->buffer, inbuf->off); + if (res == 0) { + /* We drain the input buffer on success */ + buffer_drain(inbuf, inbuf->off); + } + + return (res); +} + + +/* Reads data from an event buffer and drains the bytes read */ + +int +buffer_remove(struct buffer *buf, void *data, size_t datlen) +{ + size_t nread = datlen; + if (nread >= buf->off) + nread = buf->off; + + memcpy(data, buf->buffer, nread); + buffer_drain(buf, nread); + + return (nread); +} + +/* + * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'. + * The returned buffer needs to be freed by the called. + */ + +char * +buffer_readline(struct buffer *buffer) +{ + u_char *data = BUFFER_DATA(buffer); + size_t len = BUFFER_LENGTH(buffer); + char *line; + unsigned int i; + + for (i = 0; i < len; i++) { + if (data[i] == '\r' || data[i] == '\n') + break; + } + + if (i == len) + return (NULL); + + if ((line = malloc(i + 1)) == NULL) { + fprintf(stderr, "%s: out of memory\n", __func__); + buffer_drain(buffer, i); + return (NULL); + } + + memcpy(line, data, i); + line[i] = '\0'; + + /* + * Some protocols terminate a line with '\r\n', so check for + * that, too. + */ + if ( i < len - 1 ) { + char fch = data[i], sch = data[i+1]; + + /* Drain one more character if needed */ + if ( (sch == '\r' || sch == '\n') && sch != fch ) + i += 1; + } + + buffer_drain(buffer, i + 1); + + return (line); +} + +/* Adds data to an event buffer */ + +static void +buffer_align(struct buffer *buf) +{ + memmove(buf->orig_buffer, buf->buffer, buf->off); + buf->buffer = buf->orig_buffer; + buf->misalign = 0; +} + +/* Expands the available space in the event buffer to at least datlen */ + +int +buffer_expand(struct buffer *buf, size_t datlen) +{ + size_t need = buf->misalign + buf->off + datlen; + + /* If we can fit all the data, then we don't have to do anything */ + if (buf->totallen >= need) + return (0); + + /* + * If the misalignment fulfills our data needs, we just force an + * alignment to happen. Afterwards, we have enough space. + */ + if (buf->misalign >= datlen) { + buffer_align(buf); + } else { + void *newbuf; + size_t length = buf->totallen; + + if (length < 256) + length = 256; + while (length < need) + length <<= 1; + + if (buf->orig_buffer != buf->buffer) + buffer_align(buf); + if ((newbuf = realloc(buf->buffer, length)) == NULL) + return (-1); + + buf->orig_buffer = buf->buffer = newbuf; + buf->totallen = length; + } + + return (0); +} + +int +buffer_add(struct buffer *buf, const void *data, size_t datlen) +{ + size_t need = buf->misalign + buf->off + datlen; + size_t oldoff = buf->off; + + if (buf->totallen < need) { + if (buffer_expand(buf, datlen) == -1) + return (-1); + } + + memcpy(buf->buffer + buf->off, data, datlen); + buf->off += datlen; + + if (datlen && buf->cb != NULL) + (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); + + return (0); +} + +void +buffer_drain(struct buffer *buf, size_t len) +{ + size_t oldoff = buf->off; + + if (len >= buf->off) { + buf->off = 0; + buf->buffer = buf->orig_buffer; + buf->misalign = 0; + goto done; + } + + buf->buffer += len; + buf->misalign += len; + + buf->off -= len; + + done: + /* Tell someone about changes in this buffer */ + if (buf->off != oldoff && buf->cb != NULL) + (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); + +} + +/* + * Reads data from a file descriptor into a buffer. + */ + +#define BUFFER_MAX_READ 4096 + +int +buffer_read(struct buffer *buf, int fd, int howmuch) +{ + u_char *p; + size_t oldoff = buf->off; + int n = BUFFER_MAX_READ; + +#if defined(FIONREAD) +#ifdef WIN32 + unsigned long lng = n; + if (ioctlsocket(fd, FIONREAD, &lng) == -1 || (n=lng) == 0) { +#else + if (ioctl(fd, FIONREAD, &n) == -1 || n == 0) { +#endif + n = BUFFER_MAX_READ; + } else if (n > BUFFER_MAX_READ && n > howmuch) { + /* + * It's possible that a lot of data is available for + * reading. We do not want to exhaust resources + * before the reader has a chance to do something + * about it. If the reader does not tell us how much + * data we should read, we artifically limit it. + */ + if (n > buf->totallen << 2) + n = buf->totallen << 2; + if (n < BUFFER_MAX_READ) + n = BUFFER_MAX_READ; + } +#endif + if (howmuch < 0 || howmuch > n) + howmuch = n; + + /* If we don't have FIONREAD, we might waste some space here */ + if (buffer_expand(buf, howmuch) == -1) + return (-1); + + /* We can append new data at this point */ + p = buf->buffer + buf->off; + +#ifndef WIN32 + n = read(fd, p, howmuch); +#else + n = recv(fd, (char *)p, howmuch, 0); +#endif + if (n == -1) + return (-1); + if (n == 0) + return (0); + + buf->off += n; + + /* Tell someone about changes in this buffer */ + if (buf->off != oldoff && buf->cb != NULL) + (*buf->cb)(buf, oldoff, buf->off, buf->cbarg); + + return (n); +} + +int +buffer_write(struct buffer *buffer, int fd) +{ + int n; + +#ifndef WIN32 + n = write(fd, buffer->buffer, buffer->off); +#else + n = send(fd, (char *)buffer->buffer, buffer->off, 0); +#endif + if (n == -1) + return (-1); + if (n == 0) + return (0); + buffer_drain(buffer, n); + + return (n); +} + +u_char * +buffer_find(struct buffer *buffer, const u_char *what, size_t len) +{ + u_char *search = buffer->buffer, *end = search + buffer->off; + u_char *p; + + while (search < end && + (p = memchr(search, *what, end - search)) != NULL) { + if (p + len > end) + break; + if (memcmp(p, what, len) == 0) + return (p); + search = p + 1; + } + + return (NULL); +} + +void buffer_setcb(struct buffer *buffer, + void (*cb)(struct buffer *, size_t, size_t, void *), + void *cbarg) +{ + buffer->cb = cb; + buffer->cbarg = cbarg; +} diff --git a/lib/buffer.h b/lib/buffer.h new file mode 100644 index 0000000..e789990 --- /dev/null +++ b/lib/buffer.h @@ -0,0 +1,193 @@ +#ifndef BUFFER_H +#define BUFFER_H + +#include + +#ifdef WIN32 +typedef unsigned char u_char; +typedef unsigned short u_short; +#endif + + +struct buffer { + u_char *buffer; + u_char *orig_buffer; + + size_t misalign; + size_t totallen; + size_t off; + + void (*cb)(struct buffer *, size_t, size_t, void *); + void *cbarg; +}; + +/* Just for error reporting - use other constants otherwise */ +#define BUFFER_READ 0x01 +#define BUFFER_WRITE 0x02 +#define BUFFER_EOF 0x10 +#define BUFFER_ERROR 0x20 +#define BUFFER_TIMEOUT 0x40 + +#define BUFFER_LENGTH(x) (x)->off +#define BUFFER_DATA(x) (x)->buffer +#define BUFFER_INPUT(x) (x)->input +#define BUFFER_OUTPUT(x) (x)->output + +/** + Allocate storage for a new buffer. + + @return a pointer to a newly allocated buffer struct, or NULL if an error + occurred + */ +struct buffer *buffer_new(void); + + +/** + Deallocate storage for an buffer. + + @param pointer to the buffer to be freed + */ +void buffer_free(struct buffer *); + + +/** + Expands the available space in an event buffer. + + Expands the available space in the event buffer to at least datlen + + @param buf the event buffer to be expanded + @param datlen the new minimum length requirement + @return 0 if successful, or -1 if an error occurred +*/ +int buffer_expand(struct buffer *, size_t); + + +/** + Append data to the end of an buffer. + + @param buf the event buffer to be appended to + @param data pointer to the beginning of the data buffer + @param datlen the number of bytes to be copied from the data buffer + */ +int buffer_add(struct buffer *, const void *, size_t); + + + +/** + Read data from an event buffer and drain the bytes read. + + @param buf the event buffer to be read from + @param data the destination buffer to store the result + @param datlen the maximum size of the destination buffer + @return the number of bytes read + */ +int buffer_remove(struct buffer *, void *, size_t); + + +/** + * Read a single line from an event buffer. + * + * Reads a line terminated by either '\r\n', '\n\r' or '\r' or '\n'. + * The returned buffer needs to be freed by the caller. + * + * @param buffer the buffer to read from + * @return pointer to a single line, or NULL if an error occurred + */ +char *buffer_readline(struct buffer *); + + +/** + Move data from one buffer into another buffer. + + This is a destructive add. The data from one buffer moves into + the other buffer. The destination buffer is expanded as needed. + + @param outbuf the output buffer + @param inbuf the input buffer + @return 0 if successful, or -1 if an error occurred + */ +int buffer_add_buffer(struct buffer *, struct buffer *); + + +/** + Append a formatted string to the end of an buffer. + + @param buf the buffer that will be appended to + @param fmt a format string + @param ... arguments that will be passed to printf(3) + @return The number of bytes added if successful, or -1 if an error occurred. + */ +int buffer_add_printf(struct buffer *, const char *fmt, ...) +#ifdef __GNUC__ + __attribute__((format(printf, 2, 3))) +#endif +; + + +/** + Append a va_list formatted string to the end of an buffer. + + @param buf the buffer that will be appended to + @param fmt a format string + @param ap a varargs va_list argument array that will be passed to vprintf(3) + @return The number of bytes added if successful, or -1 if an error occurred. + */ +int buffer_add_vprintf(struct buffer *, const char *fmt, va_list ap); + + +/** + Remove a specified number of bytes data from the beginning of an buffer. + + @param buf the buffer to be drained + @param len the number of bytes to drain from the beginning of the buffer + @return 0 if successful, or -1 if an error occurred + */ +void buffer_drain(struct buffer *, size_t); + + +/** + Write the contents of an buffer to a file descriptor. + + The buffer will be drained after the bytes have been successfully written. + + @param buffer the buffer to be written and drained + @param fd the file descriptor to be written to + @return the number of bytes written, or -1 if an error occurred + @see buffer_read() + */ +int buffer_write(struct buffer *, int); + + +/** + Read from a file descriptor and store the result in an buffer. + + @param buf the buffer to store the result + @param fd the file descriptor to read from + @param howmuch the number of bytes to be read + @return the number of bytes read, or -1 if an error occurred + @see buffer_write() + */ +int buffer_read(struct buffer *, int, int); + + +/** + Find a string within an buffer. + + @param buffer the buffer to be searched + @param what the string to be searched for + @param len the length of the search string + @return a pointer to the beginning of the search string, or NULL if the search failed. + */ +u_char *buffer_find(struct buffer *, const u_char *, size_t); + +/** + Set a callback to invoke when the buffer is modified. + + @param buffer the buffer to be monitored + @param cb the callback function to invoke when the buffer is modified + @param cbarg an argument to be provided to the callback function + */ +void buffer_setcb(struct buffer *, void (*)(struct buffer *, size_t, size_t, void *), void *); + + +#endif diff --git a/lib/buildin-types.c.template b/lib/buildin-types.c.template new file mode 100644 index 0000000..0380f25 --- /dev/null +++ b/lib/buildin-types.c.template @@ -0,0 +1,36 @@ +/*** BEGIN file-header ***/ +#include +#include + +#include +/*** END file-header ***/ + +/*** BEGIN file-production ***/ +/* enumerations from "@filename@" */ +/*** END file-production ***/ + +/*** BEGIN value-header ***/ +GType +@enum_name@_get_type (void) +{ + static GType etype = 0; + if (G_UNLIKELY(etype == 0)) { + static const G@Type@Value values[] = { +/*** END value-header ***/ + +/*** BEGIN value-production ***/ + { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, +/*** END value-production ***/ + +/*** BEGIN value-tail ***/ + { 0, NULL, NULL } + }; + etype = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); + } + return etype; +} + +/*** END value-tail ***/ + +/*** BEGIN file-tail ***/ +/*** END file-tail ***/ diff --git a/lib/ccnet-client.c b/lib/ccnet-client.c new file mode 100644 index 0000000..a91f544 --- /dev/null +++ b/lib/ccnet-client.c @@ -0,0 +1,949 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "option.h" +#include "include.h" + + +#include +#include +#include + +#ifdef WIN32 + #include + #include + #include +#else + #include + #include + #include + #include + #include +#endif + +#include "message.h" + +#include "processor.h" +#include "packet-io.h" +#include "peer.h" +#include "ccnet-client.h" + +#include "proc-factory.h" +#include "job-mgr.h" + +#include "ccnet-object.h" + +/** + * SECTION:ccnet-client + * @short_description: The basic class to interact with the ccnet daemon + * @title: CcnetClient + * @include: ccnet.h + * + * CcnetClient is the basic class to interact with the ccnet daemon. + * CcnetClient can work in two different mode, i.e., CCNET_CLIENT_SYNC + * and CCNET_CLIENT_ASYNC (See #CcnetClientMode). The two modes have + * different sets of APIs. + * + */ + +G_DEFINE_TYPE (CcnetClient, ccnet_client, CCNET_TYPE_SESSION_BASE); + + + +static void handle_packet (ccnet_packet *packet, void *vclient); +static void ccnet_client_free (GObject *object); +static void free_rpc_pool (CcnetClient *client); + + +static void +set_property (GObject *object, guint property_id, + const GValue *v, GParamSpec *pspec) +{ + /* CcnetClient *client = CCNET_CLIENT (object); */ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + return; + } +} + +static void +get_property (GObject *object, guint property_id, + GValue *v, GParamSpec *pspec) +{ + /* CcnetClient *client = CCNET_CLIENT (object); */ + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +ccnet_client_class_init (CcnetClientClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = set_property; + gobject_class->get_property = get_property; + gobject_class->finalize = ccnet_client_free; + + /* ccnet_object_init (); */ +} + +static void +ccnet_client_init (CcnetClient *client) +{ + client->connfd = -1; + client->req_id = CCNET_USER_ID_START; + client->processors = g_hash_table_new_full (g_int_hash, g_int_equal, + g_free, NULL); + client->proc_factory = ccnet_proc_factory_new (client); + /* job_mgr should be created by user, so that max_thread can + * be chosen properly. */ +} + +CcnetClient* +ccnet_client_new (void) +{ + return g_object_new (CCNET_TYPE_CLIENT, NULL); +} + +static void +ccnet_client_free (GObject *object) +{ + CcnetClient *client = CCNET_CLIENT (object); + if (client->io) + ccnet_client_disconnect_daemon (client); + + if (client->config_dir) + free (client->config_dir); + g_free (client->config_file); + if (client->proc_factory) + g_object_unref (client->proc_factory); + if (client->job_mgr) + ccnet_job_manager_free (client->job_mgr); + if (client->processors) + g_hash_table_destroy (client->processors); + + free_rpc_pool (client); + + G_OBJECT_CLASS(ccnet_client_parent_class)->finalize (object); +} + +int +ccnet_client_load_confdir (CcnetClient *client, const char *central_config_dir_r, const char *config_dir_r) +{ + char *config_file = NULL, *config_dir = NULL, *central_config_dir = NULL; + char *id = NULL, *name = NULL, *port_str = NULL, *un_path = NULL, + *user_name = NULL, *service_url = NULL; + unsigned char sha1[20]; + GKeyFile *key_file; + CcnetSessionBase *base = CCNET_SESSION_BASE(client); + + config_dir = ccnet_util_expand_path (config_dir_r); + + if (ccnet_util_checkdir(config_dir) < 0) { + g_warning ("Config dir %s does not exist or is not " + "a directory.\n", config_dir); + return -1; + } + + if (central_config_dir_r) { + central_config_dir = ccnet_util_expand_path (central_config_dir_r); + if (ccnet_util_checkdir(config_dir) < 0) { + g_warning ("Server config dir %s does not exist or is not " + "a directory.\n", central_config_dir); + return -1; + } + } + + config_file = + g_strconcat(central_config_dir ? central_config_dir : config_dir, "/", + SESSION_CONFIG_FILENAME, NULL); + ccnet_debug ("using config file %s\n", config_file); + key_file = g_key_file_new(); + if (!g_key_file_load_from_file (key_file, config_file, + G_KEY_FILE_KEEP_COMMENTS, NULL)) + { + g_warning ("Can't load config file %s.\n", config_file); + goto onerror; + } + + id = ccnet_util_key_file_get_string (key_file, "General", "ID"); + user_name = ccnet_util_key_file_get_string (key_file, "General", "USER_NAME"); + name = ccnet_util_key_file_get_string (key_file, "General", "NAME"); + service_url = ccnet_util_key_file_get_string (key_file, "General", "SERVICE_URL"); + port_str = ccnet_util_key_file_get_string (key_file, "Client", "PORT"); + un_path = ccnet_util_key_file_get_string (key_file, "Client", "UNIX_SOCKET"); + + if ( (id == NULL) || (strlen (id) != SESSION_ID_LENGTH) + || (ccnet_util_hex_to_sha1 (id, sha1) < 0) ) + { + ccnet_error ("Wrong ID\n"); + g_key_file_free (key_file); + goto onerror; + } + + + memcpy (base->id, id, 40); + base->id[40] = '\0'; + base->user_name = g_strdup(user_name); + base->name = g_strdup(name); + memcpy (base->id_sha1, sha1, 20); + if (service_url) + base->service_url = g_strdup(service_url); + + client->config_file = g_strdup(config_file); + client->config_dir = config_dir; + client->central_config_dir = central_config_dir; + + if (port_str) + client->daemon_port = atoi (port_str); + client->un_path = un_path; + + g_free (id); + g_free (name); + g_free (user_name); + g_free (port_str); + g_free (config_file); + g_free (service_url); + g_key_file_free (key_file); + return 0; + +onerror: + g_free (id); + g_free (name); + g_free (user_name); + g_free (port_str); + g_free (config_file); + g_free (service_url); + return -1; +} + + +int +ccnet_client_connect_daemon (CcnetClient *client, CcnetClientMode mode) +{ + evutil_socket_t sockfd; + /* CcnetProcessor *processor; */ + +#ifdef WIN32 + static int inited = 0; + if (inited == 0) { + inited = 1; + WSADATA wsadata; + WSAStartup(0x0101, &wsadata); + } +#endif + + g_return_val_if_fail (client->connected == 0, -1); + + client->mode = mode; + +#ifdef WIN32 + sockfd = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in servaddr; + memset (&servaddr, 0, sizeof(servaddr)); + servaddr.sin_family = AF_INET; + servaddr.sin_port = htons (client->daemon_port); + ccnet_util_inet_pton (AF_INET, "127.0.0.1", &servaddr.sin_addr); + if (connect (sockfd, (struct sockaddr *) &servaddr, (socklen_t)sizeof(servaddr)) < 0) + return -1; +#else + char *un_path = NULL; + + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + struct sockaddr_un servaddr; + servaddr.sun_family = AF_UNIX; + + if (!client->un_path) + un_path = g_build_filename (client->config_dir, CCNET_PIPE_NAME, NULL); + else + un_path = g_strdup(client->un_path); + + g_strlcpy (servaddr.sun_path, un_path, sizeof(servaddr.sun_path)); + g_free (un_path); + if (connect(sockfd, (struct sockaddr *)&servaddr, (socklen_t)sizeof(servaddr)) < 0) { + return -1; + } +#endif + + client->connfd = sockfd; + client->io = ccnet_packet_io_new (client->connfd); + + if (mode == CCNET_CLIENT_ASYNC) + ccnet_packet_io_set_callback (client->io, handle_packet, client); + + client->connected = 1; + + g_debug ("connected to daemon\n"); + + return client->connfd; +} + +void +ccnet_client_run_synchronizer (CcnetClient *client) +{ + g_return_if_fail(client->mode == CCNET_CLIENT_ASYNC); +} + + +int +ccnet_client_disconnect_daemon (CcnetClient *client) +{ + ccnet_packet_io_free (client->io); + client->io = NULL; + client->connfd = -1; + client->connected = 0; + free_rpc_pool (client); + + return 0; +} + +uint32_t +ccnet_client_get_request_id (CcnetClient *client) +{ + return (++client->req_id); +} + +typedef struct RpcPoolItem { + uint32_t req_id; + char *peer_id; + char *service; +} RpcPoolItem; + +static void +free_rpc_pool_item (RpcPoolItem *item) +{ + g_free (item->peer_id); + g_free (item->service); + g_free (item); +} + +static void +free_rpc_pool (CcnetClient *client) +{ + GList *ptr; + for (ptr = client->rpc_pool; ptr; ptr = ptr->next) { + RpcPoolItem *item = ptr->data; + free_rpc_pool_item (item); + } + g_list_free (client->rpc_pool); + client->rpc_pool = NULL; +} + +static RpcPoolItem * +get_pool_item (CcnetClient *client, const char *peer_id, + const char *service) +{ + GList *ptr; + for (ptr = client->rpc_pool; ptr; ptr = ptr->next) { + RpcPoolItem *item = ptr->data; + if (g_strcmp0(peer_id, item->peer_id) == 0 && + g_strcmp0(service, item->service) == 0) + return item; + } + return NULL; +} + +static uint32_t +start_request (CcnetClient *client, const char *peer_id, + const char *service) +{ + uint32_t req_id = ccnet_client_get_request_id (client); + char buf[512]; + + if (!peer_id) + snprintf (buf, 512, "%s", service); + else + snprintf (buf, 512, "remote %s %s", peer_id, service); + ccnet_client_send_request (client, req_id, buf); + + if (ccnet_client_read_response (client) < 0) { + g_warning ("[RPC] failed to read response.\n"); + return 0; + } + + if (memcmp (client->response.code, "200", 3) != 0) { + g_warning ("[RPC] failed to start rpc server: %s %s.\n", + client->response.code, client->response.code_msg); + return 0; + } + + return req_id; +} + +uint32_t +ccnet_client_get_rpc_request_id (CcnetClient *client, const char *peer_id, + const char *service) +{ + RpcPoolItem *item = get_pool_item (client, peer_id, service); + if (item) + return item->req_id; + + uint32_t req_id = start_request (client, peer_id, service); + if (req_id == 0) + return 0; + + item = g_new0 (RpcPoolItem, 1); + item->req_id = req_id; + item->peer_id = g_strdup (peer_id); + item->service = g_strdup (service); + client->rpc_pool = g_list_prepend (client->rpc_pool, item); + return req_id; +} + +void +ccnet_client_clean_rpc_request (CcnetClient *client, uint32_t req_id) +{ + GList *ptr; + RpcPoolItem *target = NULL; + + for (ptr = client->rpc_pool; ptr; ptr = ptr->next) { + RpcPoolItem *item = ptr->data; + if (req_id == item->req_id) + target = item; + } + if (!target) return; + + client->rpc_pool = g_list_remove (client->rpc_pool, target); + free_rpc_pool_item (target); +} + + +/* functions used in ASYNC mode */ +void +ccnet_client_add_processor (CcnetClient *client, CcnetProcessor *processor) +{ + int *key = g_new0 (int, 1); + + *key = processor->id; + g_hash_table_insert (client->processors, key, processor); +} + +void +ccnet_client_remove_processor (CcnetClient *client, CcnetProcessor *processor) +{ + g_hash_table_remove (client->processors, &processor->id); +} + +CcnetProcessor * +ccnet_client_get_processor (CcnetClient *client, int id) +{ + return g_hash_table_lookup (client->processors, &id); +} + + +int +ccnet_client_read_input (CcnetClient *client) +{ + if (!client->io) + return -1; + return ccnet_packet_io_read(client->io); +} + +static void create_processor (CcnetClient *client, int req_id, + int argc, char **argv) +{ + CcnetProcessor *processor; + CcnetProcFactory *factory = client->proc_factory; + char *peer_id; + + if (strcmp(argv[0], "remote") == 0) { + peer_id = argv[1]; + argc -= 2; + argv += 2; + } else + peer_id = client->base.id; + + processor = ccnet_proc_factory_create_slave_processor ( + factory, argv[0], peer_id, req_id); + + if (processor) { + ccnet_processor_start (processor, argc-1, argv+1); + } else + ccnet_client_send_response (client, req_id, + SC_CREATE_PROC_ERR, + SS_CREATE_PROC_ERR, + NULL, 0); +} + +#if 0 +void +ccnet_client_send_event (CcnetClient *client, GObject *event) +{ + if (!event) return; + CcnetProcessor *processor = NULL; + processor = ccnet_proc_factory_create_master_processor + (client->proc_factory, "send-event"); + ccnet_sendevent_proc_set_event (CCNET_SENDEVENT_PROC(processor), + (CcnetEvent *)event); + ccnet_processor_start (processor, 0, NULL); +} +#endif + +static void +handle_request (CcnetClient *client, int req_id, char *data, int len) +{ + char *msg; + gchar **commands; + gchar **pcmd; + int i; + + /* TODO: remove string copy */ + g_return_if_fail (len >= 1); + msg = g_malloc (len+1); + memcpy (msg, data, len); + msg[len] = '\0'; + + commands = g_strsplit_set (msg, " \t", 10); + for (i=0, pcmd = commands; *pcmd; pcmd++) + i++; + g_free (msg); + + create_processor (client, req_id, i, commands); + g_strfreev (commands); +} + + +static void +handle_response (CcnetClient *client, int req_id, char *data, int len) +{ + CcnetProcessor *processor; + char *code, *code_msg = 0, *content = 0; + int clen; + char *ptr, *end; + + g_return_if_fail (len >= 4); + + code = data; + + ptr = data + 3; + if (*ptr == '\n') { + /* no code_msg */ + *ptr++ = '\0'; + content = ptr; + clen = len - (ptr - data); + goto parsed; + } + + if (*ptr != ' ') + goto error; + + *ptr++ = '\0'; + code_msg = ptr; + + end = data + len; + for (ptr = data; *ptr != '\n' && ptr != end; ptr++) ; + + if (ptr == end) /* must end with '\n' */ + goto error; + + /* if (*(ptr-1) == '\r') */ + /* *(ptr-1) = '\0'; */ + *ptr++ = '\0'; + content = ptr; + clen = len - (ptr - data); + +parsed: + /* ccnet_message ("Receive response %s from %s\n", msg, peer->id); */ + /* ccnet_message ("code %s, code_msg %s, content %s\n", code, code_msg, */ + /* content); */ + processor = ccnet_client_get_processor (client, MASTER_ID (req_id)); + if (processor == NULL) { + if (strcmp (code, SC_PROC_DEAD) != 0) { + ccnet_debug ("Delayed response from daemon, id is %d, %s %s\n", + MASTER_ID(req_id), code, code_msg); + ccnet_client_send_update (client, req_id, + SC_PROC_DEAD, SS_PROC_DEAD, + NULL, 0); + } + return; + } + + /* ccnet_debug ("[client] handle_response %s id is %d, %s %s\n", */ + /* GET_PNAME(processor), req_id, code, code_msg); */ + + ccnet_processor_handle_response (processor, code, code_msg, content, clen); + return; + +error: + g_warning ("Bad response format from daemon\n"); +} + +static void +handle_update (CcnetClient *client, int req_id, char *data, int len) +{ + CcnetProcessor *processor; + char *code, *code_msg = 0, *content = 0; + int clen; + char *ptr, *end; + + g_return_if_fail (len >= 4); + + code = data; + + ptr = data + 3; + if (*ptr == '\n') { + /* no code_msg */ + *ptr++ = '\0'; + content = ptr; + clen = len - (ptr - data); + goto parsed; + } + + if (*ptr != ' ') + goto error; + + *ptr++ = '\0'; + code_msg = ptr; + + end = data + len; + for (ptr = data; *ptr != '\n' && ptr != end; ptr++) ; + + if (ptr == end) /* must end with '\n' */ + goto error; + + /* if (*(ptr-1) == '\r') */ + /* *(ptr-1) = '\0'; */ + *ptr++ = '\0'; + content = ptr; + clen = len - (ptr - data); + +parsed: + processor = ccnet_client_get_processor (client, SLAVE_ID(req_id)); + if (processor == NULL) { + if (strcmp (code, SC_PROC_DEAD) != 0) { + ccnet_debug ("Delayed update from daemon, id is %d, %s %s\n", + req_id, code, code_msg); + ccnet_client_send_response (client, req_id, + SC_PROC_DEAD, SS_PROC_DEAD, + NULL, 0); + } + return; + } + /* ccnet_debug ("[client] handle_update %s id is %d, %s %s\n", */ + /* GET_PNAME(processor), req_id, code, code_msg); */ + + ccnet_processor_handle_update (processor, code, code_msg, content, clen); + return; + +error: + g_warning ("Bad update format\n"); +} + + +static void handle_packet (ccnet_packet *packet, void *vclient) +{ + CcnetClient *client = vclient; + + if (packet == NULL) { + /* disconnected from daemon */ + g_warning ("Disconnected from daemon\n"); + return; + } + + switch (packet->header.type) { + case CCNET_MSG_REQUEST: + handle_request (client, packet->header.id, + packet->data, packet->header.length); + break; + case CCNET_MSG_RESPONSE: + handle_response (client, packet->header.id, + packet->data, packet->header.length); + break; + case CCNET_MSG_UPDATE: + handle_update (client, packet->header.id, + packet->data, packet->header.length); + break; + default: + g_return_if_reached (); + } +} + + +void +ccnet_client_send_request (CcnetClient *client, int req_id, const char *req) +{ + ccnet_packet_prepare (client->io, CCNET_MSG_REQUEST, req_id); + ccnet_packet_write_string (client->io, req); + ccnet_packet_finish_send (client->io); + + g_debug ("Send a request: id %d, cmd %s\n", req_id, req); +} + +/** + * ccnet_client_send_update: + * @client: + * @req_id: request id + * @code: A string of three numbers. Like "200" + * @reason: long description for @code, can't contain '\n', can be %NULL + * @content: A char array, can be %NULL. + * @clen: length of %content, in bytes. + */ +void +ccnet_client_send_update (CcnetClient *client, int req_id, + const char *code, const char *reason, + const char *content, int clen) +{ + g_return_if_fail (req_id > 0); + g_return_if_fail (clen < CCNET_PACKET_MAX_PAYLOAD_LEN); + + ccnet_packet_prepare (client->io, CCNET_MSG_UPDATE, req_id); + /* code line */ + ccnet_packet_add (client->io, code, 3); + if (reason) { + ccnet_packet_add (client->io, " ", 1); + ccnet_packet_write_string (client->io, reason); + } + ccnet_packet_add (client->io, "\n", 1); + if (content) + ccnet_packet_add (client->io, content, clen); + + ccnet_packet_finish_send (client->io); + + /* g_debug ("[client] Send an update: id %d: %s %s len=%d\n", */ + /* req_id, code, reason, clen); */ +} + + +void +ccnet_client_send_response (CcnetClient *client, int req_id, + const char *code, const char *reason, + const char *content, int clen) +{ + g_return_if_fail (clen < CCNET_PACKET_MAX_PAYLOAD_LEN); + + ccnet_packet_prepare (client->io, CCNET_MSG_RESPONSE, req_id); + /* code line */ + ccnet_packet_add (client->io, code, 3); + if (reason) { + ccnet_packet_add (client->io, " ", 1); + ccnet_packet_write_string (client->io, reason); + } + ccnet_packet_add (client->io, "\n", 1); + if (content) + ccnet_packet_add (client->io, content, clen); + + ccnet_packet_finish_send (client->io); + + /* g_debug ("[client] Send an response: id %d: %s %s len=%d\n", */ + /* req_id, code, reason, clen); */ +} + + +/* functions used in SYNC mode */ + +/** + * ccnet_client_read_response: + * @client: + * + * Read response from the daemon. The response can be accessed by + * client->response. + * + * Returns: -1 if io error, -2 if response packet format error + */ +int +ccnet_client_read_response (CcnetClient *client) +{ + ccnet_packet *packet; + char *data; + int len, clen; + char *code, *code_msg = 0, *content = 0; + char *ptr, *end; + +restart: + if ( (packet = ccnet_packet_io_read_packet (client->io)) == NULL) + return -1; + + if (packet->header.type != CCNET_MSG_RESPONSE) + goto error; + + data = packet->data; + len = packet->header.length; + + g_return_val_if_fail (len >= 4, -1); + + code = data; + + ptr = data + 3; + if (*ptr == '\n') { + /* no code_msg */ + *ptr++ = '\0'; + content = ptr; + clen = len - (ptr - data); + goto parsed; + } + + if (*ptr != ' ') + goto error; + + *ptr++ = '\0'; + code_msg = ptr; + + end = data + len; + for (ptr = data; *ptr != '\n' && ptr != end; ptr++) ; + + if (ptr == end) /* must end with '\n' */ + goto error; + + /* if (*(ptr-1) == '\r') */ + /* *(ptr-1) = '\0'; */ + *ptr++ = '\0'; + content = ptr; + clen = len - (ptr - data); + + /* In synchronized mode, we only have one processor at a + time. The processor id is client->req_id, other + processors are all treat as dead. */ + + /* + if (packet->header.id != client->req_id) { + if (strcmp (code, SC_PROC_DEAD) != 0) { + g_debug ("Read response error: want %d, get %d %s %s\n", + client->req_id, packet->header.id, code, code_msg); + ccnet_client_send_update(client, packet->header.id, + SC_PROC_DEAD, SS_PROC_DEAD, NULL, 0); + } + goto restart; + } + */ + + /* handle processor keep alive response */ + if (strncmp(code, SC_PROC_KEEPALIVE, 3) == 0) { + ccnet_client_send_update(client, packet->header.id, + SC_PROC_ALIVE, SS_PROC_ALIVE, NULL, 0); + goto restart; + } + + +parsed: + client->response.code = code; + client->response.code_msg = code_msg; + client->response.content = content; + client->response.clen = clen; + return 0; + +error: + g_warning ("Bad response format from daemon\n"); + return -2; +} + +static int read_response_common(CcnetClient *client) +{ + if (ccnet_client_read_response(client) < 0) + return -1; + if (client->response.code[0] == '4' || + client->response.code[0] == '5') { + g_warning ("Error response from daemon: %s %s\n", + client->response.code, client->response.code_msg); + return -1; + } + + return 0; +} + +static gboolean +check_response_error (CcnetClient *client, GError **error) +{ + if (client->response.code[0] == '4' || + client->response.code[0] == '5') { + g_set_error (error, CCNET_DOMAIN, atoi(client->response.code), + "%s", client->response.code_msg); + return TRUE; + } + return FALSE; +} + + +const char * +ccnet_client_send_cmd (CcnetClient *client, const char *cmd, GError **error) +{ + int req_id = ccnet_client_get_request_id (client); + ccnet_client_send_request (client, req_id, "receive-cmd"); + + if (ccnet_client_read_response(client) < 0) { + g_set_error (error, CCNET_DOMAIN, EC_NETWORK_ERR, "%s", ES_NETWORK_ERR); + goto on_error; + } + + ccnet_client_send_update (client, req_id, + "200", NULL, cmd, strlen(cmd) + 1); + if (ccnet_client_read_response(client) < 0) { + g_set_error (error, CCNET_DOMAIN, EC_NETWORK_ERR, "%s", ES_NETWORK_ERR); + goto on_error; + } + + if (check_response_error(client, error)) { + goto on_error; + } + + ccnet_client_send_update (client, req_id, + SC_PROC_DONE, SS_PROC_DONE, + NULL, 0); + return client->response.content; + +on_error: + ccnet_client_send_update (client, req_id, + SC_PROC_DONE, SS_PROC_DONE, + NULL, 0); + return NULL; +} + + +#define SC_MSG "300" + +int +ccnet_client_send_message (CcnetClient *client, + CcnetMessage *message) +{ + GString *buf; + int req_id = ccnet_client_get_request_id (client); + + ccnet_client_send_request (client, req_id, "mq-server"); + + if (ccnet_client_read_response (client) < 0) + return -1; /* TODO: handle response code */ + + buf = g_string_new (NULL); + ccnet_message_to_string_buf (message, buf); + ccnet_client_send_update (client, req_id, + SC_MSG, NULL, buf->str, buf->len+1); + if (ccnet_client_read_response (client) < 0) + return -1; + + g_string_free (buf, TRUE); + return 0; +} + +int +ccnet_client_prepare_recv_message (CcnetClient *client, + const char *app) +{ + int req_id = ccnet_client_get_request_id (client); + char buf[256]; + + snprintf (buf, sizeof(buf), "mq-server %s", app); + ccnet_client_send_request (client, req_id, buf); + + if (read_response_common (client) < 0) + return -1; + + if (memcmp(client->response.code, "200", 3) != 0) + return -1; + + return 0; +} + +CcnetMessage * +ccnet_client_receive_message (CcnetClient *client) +{ + CcnetMessage *message; + + if (read_response_common (client) < 0) + return NULL; + + message = ccnet_message_from_string (client->response.content, + client->response.clen); + return message; +} diff --git a/lib/ccnet-rpc-wrapper.c b/lib/ccnet-rpc-wrapper.c new file mode 100644 index 0000000..e2064b9 --- /dev/null +++ b/lib/ccnet-rpc-wrapper.c @@ -0,0 +1,325 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "include.h" + +#include +#include +#include +#include + + +SearpcClient * +ccnet_create_rpc_client (CcnetClient *cclient, + const char *peer_id, + const char *service_name) +{ + SearpcClient *rpc_client; + CcnetrpcTransportParam *priv; + + priv = g_new0(CcnetrpcTransportParam, 1); + priv->session = cclient; + priv->peer_id = g_strdup(peer_id); + priv->service = g_strdup(service_name); + + rpc_client = searpc_client_new (); + rpc_client->send = ccnetrpc_transport_send; + rpc_client->arg = priv; + + return rpc_client; +} + +SearpcClient * +ccnet_create_pooled_rpc_client (struct CcnetClientPool *cpool, + const char *peer_id, + const char *service) +{ + SearpcClient *rpc_client; + CcnetrpcTransportParam *priv; + + priv = g_new0(CcnetrpcTransportParam, 1); + priv->pool = cpool; + priv->peer_id = g_strdup(peer_id); + priv->service = g_strdup(service); + + rpc_client = searpc_client_new (); + rpc_client->send = ccnetrpc_transport_send; + rpc_client->arg = priv; + + return rpc_client; +} + +SearpcClient * +ccnet_create_async_rpc_client (CcnetClient *cclient, const char *peer_id, + const char *service_name) +{ + SearpcClient *rpc_client; + CcnetrpcAsyncTransportParam *async_priv; + + async_priv = g_new0 (CcnetrpcAsyncTransportParam, 1); + async_priv->session = cclient; + async_priv->peer_id = g_strdup(peer_id); + async_priv->service = g_strdup (service_name); + + rpc_client = searpc_client_new (); + + rpc_client->async_send = ccnetrpc_async_transport_send; + rpc_client->async_arg = async_priv; + + return rpc_client; +} + +void +ccnet_rpc_client_free (SearpcClient *client) +{ + CcnetrpcTransportParam *priv; + + if (!client) return; + + priv = client->arg; + + g_free (priv->peer_id); + g_free (priv->service); + g_free (priv); + + searpc_client_free (client); +} + +void +ccnet_async_rpc_client_free (SearpcClient *client) +{ + CcnetrpcAsyncTransportParam *priv = client->arg; + + g_free (priv->peer_id); + g_free (priv->service); + g_free (priv); + + searpc_client_free (client); +} + +CcnetPeer * +ccnet_get_peer (SearpcClient *client, const char *peer_id) +{ + if (!peer_id) + return NULL; + + return (CcnetPeer *) searpc_client_call__object( + client, "get_peer",CCNET_TYPE_PEER, NULL, + 1, "string", peer_id); +} + +CcnetPeer * +ccnet_get_peer_by_idname (SearpcClient *client, const char *idname) +{ + if (!idname) + return NULL; + return (CcnetPeer *) searpc_client_call__object( + client, "get_peer_by_idname", CCNET_TYPE_PEER, NULL, + 1, "string", idname); +} + +int +ccnet_get_peer_net_state (SearpcClient *client, const char *peer_id) +{ + CcnetPeer *peer; + int ret; + peer = ccnet_get_peer (client, peer_id); + if (!peer) + return PEER_DOWN; + ret = peer->net_state; + g_object_unref (peer); + return ret; +} + +int +ccnet_get_peer_bind_status (SearpcClient *client, const char *peer_id) +{ + CcnetPeer *peer; + int ret; + peer = ccnet_get_peer (client, peer_id); + if (!peer) + return BIND_UNKNOWN; + ret = peer->bind_status; + g_object_unref (peer); + return ret; +} + +CcnetPeer * +ccnet_get_default_relay (SearpcClient *client) +{ + CcnetSessionBase *base = (CcnetSessionBase *) + searpc_client_call__object( + client, "get_session_info", CCNET_TYPE_SESSION_BASE, NULL, 0); + + if (!base) + return NULL; + + CcnetPeer *relay = ccnet_get_peer (client, base->relay_id); + g_object_unref (base); + return relay; +} + +GList * +ccnet_get_peers_by_role (SearpcClient *client, const char *role) +{ + if (!role) + return NULL; + + return searpc_client_call__objlist ( + client, "get_peers_by_role", CCNET_TYPE_PEER, NULL, + 1, "string", role); +} + +char * +ccnet_get_binding_email (SearpcClient *client, const char *peer_id) +{ + if (!peer_id) + return NULL; + + return searpc_client_call__string ( + client, "get_binding_email", NULL, + 1, "string", peer_id); +} + +GList * +ccnet_get_groups_by_user (SearpcClient *client, const char *user) +{ + if (!user) + return NULL; + + return searpc_client_call__objlist ( + client, "get_groups", CCNET_TYPE_GROUP, NULL, + 1, "string", user); +} + +GList * +ccnet_get_group_members (SearpcClient *client, int group_id) +{ + return searpc_client_call__objlist ( + client, "get_group_members", CCNET_TYPE_GROUP_USER, NULL, + 1, "int", group_id); +} + +int +ccnet_org_user_exists (SearpcClient *client, int org_id, const char *user) +{ + return searpc_client_call__int (client, "org_user_exists", NULL, + 2, "int", org_id, "string", user); +} + +#if 0 +int +ccnet_get_peer_async (SearpcClient *client, const char *peer_id, + AsyncCallback callback, void *user_data) +{ + return get_peer_async (client, peer_id, callback, user_data); +} +#endif + +int +ccnet_get_binding_email_async (SearpcClient *client, const char *peer_id, + AsyncCallback callback, void *user_data) +{ + return searpc_client_async_call__string ( + client, "get_binding_email", callback, user_data, + 1, "string", peer_id); +} + +char * +ccnet_sign_message (SearpcClient *client, const char *message) +{ + if (!message) + return NULL; + + return searpc_client_call__string ( + client, "sign_message", NULL, + 1, "string", message); +} + +int +ccnet_verify_message (SearpcClient *client, + const char *message, + const char *sig_base64, + const char *peer_id) +{ + if (!message || !sig_base64 || !peer_id) + return -1; + + return searpc_client_call__int ( + client, "verify_message", NULL, + 3, "string", message, "string", sig_base64, "string", peer_id); +} + +char * +ccnet_pubkey_encrypt (SearpcClient *client, + const char *msg_base64, + const char *peer_id) +{ + if (!msg_base64 || !peer_id) + return NULL; + + return searpc_client_call__string (client, "pubkey_encrypt", NULL, 2, + "string", msg_base64, "string", peer_id); +} + +char * +ccnet_privkey_decrypt (SearpcClient *client, const char *msg_base64) +{ + if (!msg_base64) + return NULL; + + return searpc_client_call__string (client, "privkey_decrypt", NULL, 1, + "string", msg_base64); +} + +char * +ccnet_get_config (SearpcClient *client, const char *key) +{ + if (!key) + return NULL; + + return searpc_client_call__string ( + client, "get_config", NULL, + 1, "string", key); +} + +int +ccnet_set_config (SearpcClient *client, const char *key, const char *value) +{ + if (!key || !value) + return -1; + + return searpc_client_call__int ( + client, "set_config", NULL, + 2, "string", key, "string", value); +} + +void +ccnet_login_to_relay (SearpcClient *client, const char *relay_id, + const char *username, const char *passwd) +{ + searpc_client_call__int (client, "login_relay", NULL, + 3, "string", relay_id, + "string", username, "string", passwd); +} + +gboolean +ccnet_peer_is_ready (SearpcClient *client, const char *peer_id) +{ + CcnetPeer *peer; + gboolean ret; + peer = ccnet_get_peer (client, peer_id); + if (!peer) + return FALSE; + ret = peer->is_ready; + g_object_unref (peer); + return ret; +} + +int +ccnet_update_peer_address (SearpcClient *client, const char *peer_id, + const char *addr, int port) +{ + return searpc_client_call__int ( + client, "update_peer_address", NULL, + 3, "string", peer_id, "string", addr, "int", port); +} diff --git a/lib/ccnet-session-base.c b/lib/ccnet-session-base.c new file mode 100644 index 0000000..3287485 --- /dev/null +++ b/lib/ccnet-session-base.c @@ -0,0 +1,145 @@ + +#include "option.h" +#include "include.h" + +#include "ccnet-session-base.h" + +G_DEFINE_TYPE (CcnetSessionBase, ccnet_session_base, G_TYPE_OBJECT); + +enum { + P_ID = 1, + P_USER_NAME, + P_NAME, + P_PUBLIC_PORT, + P_NET_STATUS, + P_DEFAULT_RELAY, +}; + + +static void +set_property (GObject *object, guint property_id, + const GValue *v, GParamSpec *pspec) +{ + CcnetSessionBase *session = CCNET_SESSION_BASE (object); + const char *relay; + + switch (property_id) { + case P_ID: + strcpy(session->id, g_value_get_string(v)); + break; + case P_NAME: + g_free (session->name); + session->name = g_strdup (g_value_get_string(v)); + break; + case P_USER_NAME: + g_free (session->user_name); + session->user_name = g_strdup (g_value_get_string(v)); + break; + case P_PUBLIC_PORT: + session->public_port = g_value_get_int (v); + break; + case P_NET_STATUS: + session->net_status = g_value_get_int (v); + break; + case P_DEFAULT_RELAY: + relay = g_value_get_string(v); + session->relay_id = g_strdup(relay); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + return; + } + +} + +static void +get_property (GObject *object, guint property_id, + GValue *v, GParamSpec *pspec) +{ + CcnetSessionBase *session = CCNET_SESSION_BASE (object); + + switch (property_id) { + /* commont properties to session and daemon */ + case P_ID: + g_value_set_string (v, session->id); + break; + case P_USER_NAME: + g_value_set_string (v, session->user_name); + break; + case P_NAME: + g_value_set_string (v, session->name); + break; + case P_PUBLIC_PORT: + g_value_set_int (v, session->public_port); + break; + case P_NET_STATUS: + g_value_set_int (v, session->net_status); + break; + case P_DEFAULT_RELAY: + g_value_set_string (v, session->relay_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +finalize(GObject *gobject) +{ + CcnetSessionBase *s = CCNET_SESSION_BASE(gobject); + + g_free (s->user_name); + g_free (s->name); + g_free (s->relay_id); + + G_OBJECT_CLASS(ccnet_session_base_parent_class)->finalize (gobject); +} + + +static void +ccnet_session_base_class_init (CcnetSessionBaseClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = set_property; + gobject_class->get_property = get_property; + gobject_class->finalize = finalize; + + g_object_class_install_property (gobject_class, P_ID, + g_param_spec_string ("id", NULL, "ID", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_USER_NAME, + g_param_spec_string ("user-name", NULL, "User Name", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_NAME, + g_param_spec_string ("name", NULL, "Name", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_PUBLIC_PORT, + g_param_spec_int ("public-port", NULL, "Public Port", + 0, 65525, 0, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_NET_STATUS, + g_param_spec_int ("net-status", NULL, "Network Status", + 0, NET_STATUS_FULL, NET_STATUS_DOWN, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_DEFAULT_RELAY, + g_param_spec_string ("default-relay", NULL, "Default Relay", + NULL, G_PARAM_READWRITE)); +} + +static void +ccnet_session_base_init (CcnetSessionBase *sbase) +{ + +} + +CcnetSessionBase * +ccnet_session_base_new (void) +{ + return g_object_new (CCNET_TYPE_SESSION_BASE, NULL); +} diff --git a/lib/ccnetobj.vala b/lib/ccnetobj.vala new file mode 100644 index 0000000..411711b --- /dev/null +++ b/lib/ccnetobj.vala @@ -0,0 +1,79 @@ + + +namespace Ccnet { + + +public class Proc : Object { + + public string peer_name { get; set; } + + public string _name; + public string name { + get { return _name; } + set { _name = value; } + } + + public int _ctime; + public int ctime { + get { return _ctime; } + set { _ctime = value; } + } + + public int _dtime; //dead time + public int dtime { + get { return _dtime; } + set { _dtime = value; } + } + +} + +public class EmailUser : Object { + + public int id { get; set; } + public string email { get; set; } + public bool is_staff { get; set; } + public bool is_active { get; set; } + public int64 ctime { get; set; } + public string source { get; set; } + public string role { get; set; } + public string password { get; set; } +} + +public class Group : Object { + + public int id { get; set; } + public string group_name { get; set; } + public string creator_name { get; set; } + public int64 timestamp { get; set; } + public string source { get; set; } +} + +public class GroupUser : Object { + + public int group_id { get; set; } + public string user_name { get; set; } + public int is_staff { get; set; } +} + +public class Organization : Object { + + public int org_id { get; set; } + public string email { get; set; } + public int is_staff { get; set; } + public string org_name { get; set; } + public string url_prefix { get; set; } + public string creator { get; set; } + public int64 ctime { get; set; } + +} + +public class PeerStat : Object { + public string id { get; set; } + public string name { get; set; } + public string ip { get; set; } + public bool encrypt { get; set; } + public int64 last_up { get; set; } + public int proc_num { get; set; } +} + +} // namespace diff --git a/lib/ccnetrpc-transport.c b/lib/ccnetrpc-transport.c new file mode 100644 index 0000000..9da452d --- /dev/null +++ b/lib/ccnetrpc-transport.c @@ -0,0 +1,184 @@ + +#include +#include +#include + +#include +#include "rpc-common.h" +#include + +static char * +invoke_service (CcnetClient *session, + const char *peer_id, + const char *service, + const char *fcall_str, + size_t fcall_len, + size_t *ret_len) +{ + struct CcnetResponse *rsp; + uint32_t req_id; + GString *buf; + + req_id = ccnet_client_get_rpc_request_id (session, peer_id, service); + if (req_id == 0) { + *ret_len = 0; + return NULL; + } + + ccnet_client_send_update (session, req_id, + SC_CLIENT_CALL, SS_CLIENT_CALL, + fcall_str, fcall_len); + + if (ccnet_client_read_response (session) < 0) { + *ret_len = 0; + ccnet_client_clean_rpc_request (session, req_id); + return NULL; + } + rsp = &session->response; + + if (memcmp (rsp->code, SC_SERVER_RET, 3) == 0) { + *ret_len = (size_t) rsp->clen; + return g_strndup (rsp->content, rsp->clen); + } else if (memcmp (rsp->code, SC_SERVER_MORE, 3) != 0) { + g_warning ("[Sea RPC] Bad response: %s %s.\n", rsp->code, rsp->code_msg); + *ret_len = 0; + return NULL; + } + + buf = g_string_new_len (rsp->content, rsp->clen); + while (1) { + ccnet_client_send_update (session, req_id, + SC_CLIENT_MORE, SS_CLIENT_MORE, + fcall_str, fcall_len); + + if (ccnet_client_read_response (session) < 0) { + *ret_len = 0; + ccnet_client_clean_rpc_request (session, req_id); + g_string_free (buf, TRUE); + return NULL; + } + rsp = &session->response; + + if (memcmp (rsp->code, SC_SERVER_RET, 3) == 0) { + g_string_append_len (buf, rsp->content, rsp->clen); + *ret_len = buf->len; + return g_string_free (buf, FALSE); + } else if (memcmp (rsp->code, SC_SERVER_MORE, 3) == 0) { + g_string_append_len (buf, rsp->content, rsp->clen); + } else { + g_warning ("[Sea RPC] Bad response: %s %s.\n", + rsp->code, rsp->code_msg); + *ret_len = 0; + g_string_free (buf, TRUE); + return NULL; + } + } + + /* Never reach here. */ + return NULL; +} + +static CcnetClient * +create_new_client (const char *central_config_dir, const char *conf_dir) +{ + CcnetClient *client; + + client = ccnet_client_new (); + if (ccnet_client_load_confdir (client, central_config_dir, conf_dir) < 0) { + g_warning ("[Sea RPC] Failed to load conf dir.\n"); + g_object_unref (client); + return NULL; + } + if (ccnet_client_connect_daemon (client, CCNET_CLIENT_SYNC) < 0) { + g_warning ("[Sea RPC] Failed to connect ccnet.\n"); + g_object_unref (client); + return NULL; + } + + return client; +} + +char * +ccnetrpc_transport_send (void *arg, const gchar *fcall_str, + size_t fcall_len, size_t *ret_len) +{ + CcnetrpcTransportParam *priv; + CcnetClient *session, *new_session; + + g_warn_if_fail (arg != NULL && fcall_str != NULL); + + priv = (CcnetrpcTransportParam *)arg; + + if (priv->session != NULL) { + /* Use single ccnet client as transport. */ + return invoke_service (priv->session, priv->peer_id, priv->service, + fcall_str, fcall_len, ret_len); + } else { + /* Use client pool as transport. */ + + session = ccnet_client_pool_get_client (priv->pool); + if (!session) { + g_warning ("[Sea RPC] Failed to get client from pool.\n"); + *ret_len = 0; + return NULL; + } + + char *ret = invoke_service (session, priv->peer_id, priv->service, + fcall_str, fcall_len, ret_len); + if (ret != NULL) { + ccnet_client_pool_return_client (priv->pool, session); + return ret; + } + + /* If we failed to send data through the ccnet client returned by + * client pool, ccnet may have been restarted. + * In this case, we create a new ccnet client and put it into + * the client pool after use. + */ + + g_message ("[Sea RPC] Ccnet disconnected. Connect again.\n"); + + new_session = create_new_client (session->central_config_dir, session->config_dir); + if (!new_session) { + *ret_len = 0; + return NULL; + } + g_object_unref (session); + + ret = invoke_service (new_session, priv->peer_id, priv->service, + fcall_str, fcall_len, ret_len); + if (ret != NULL) + ccnet_client_pool_return_client (priv->pool, new_session); + else + g_object_unref (new_session); + + return ret; + } +} + + +int +ccnetrpc_async_transport_send (void *arg, gchar *fcall_str, + size_t fcall_len, void *rpc_priv) +{ + CcnetrpcAsyncTransportParam *priv; + CcnetClient *session; + CcnetProcessor *proc; + + g_warn_if_fail (arg != NULL && fcall_str != NULL); + + priv = (CcnetrpcAsyncTransportParam *)arg; + session = priv->session; + + if (!priv->peer_id) + proc = ccnet_proc_factory_create_master_processor ( + session->proc_factory, "async-rpc"); + else + proc = ccnet_proc_factory_create_remote_master_processor ( + session->proc_factory, "async-rpc", priv->peer_id); + + ccnet_async_rpc_proc_set_rpc ((CcnetAsyncRpcProc *)proc, priv->service, + fcall_str, fcall_len, rpc_priv); + ccnet_processor_start (proc, 0, NULL); + return 0; +} diff --git a/lib/cevent.c b/lib/cevent.c new file mode 100644 index 0000000..078c3f5 --- /dev/null +++ b/lib/cevent.c @@ -0,0 +1,105 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "include.h" +#include "cevent.h" + +#define CEVENT_SIZE (sizeof(CEvent)) + +typedef struct Handler { + cevent_handler handler; + void *handler_data; +} Handler; + +CEventManager* cevent_manager_new () +{ + CEventManager *manager; + + manager = g_new0 (CEventManager, 1); + pthread_mutex_init (&manager->mutex, NULL); + manager->handler_table = g_hash_table_new_full (g_direct_hash, + g_direct_equal, NULL, g_free); + + return manager; +} + +void pipe_callback (evutil_socket_t fd, short event, void *vmgr) +{ + CEventManager *manager = (CEventManager *) vmgr; + CEvent *cevent; + char buf[CEVENT_SIZE]; + + if (ccnet_util_pipereadn(fd, buf, CEVENT_SIZE) != CEVENT_SIZE) { + g_warning ("read pipe error\n"); + return; + } + + cevent = (CEvent *)buf; + Handler *h = g_hash_table_lookup (manager->handler_table, + (gconstpointer)(long)cevent->id); + if (h == NULL) { + g_warning ("no handler for event type %d\n", cevent->id); + return; + } + + h->handler(cevent, h->handler_data); +} + +int cevent_manager_start (CEventManager *manager) +{ + if (ccnet_util_pipe(manager->pipefd) < 0) { + g_warning ("pipe error: %s\n", strerror(errno)); + return -1; + } + + event_set (&manager->event, manager->pipefd[0], + EV_READ | EV_PERSIST, pipe_callback, manager); + event_add (&manager->event, NULL); + + return 0; +} + +uint32_t cevent_manager_register (CEventManager *manager, + cevent_handler handler, void *handler_data) +{ + uint32_t id; + Handler *h; + + h = g_new0(Handler, 1); + h->handler = handler; + h->handler_data = handler_data; + + /* Since we're using 32-bit int for id, it may wrap around to 0. + * If some caller persistently use one id, it's handler may be + * overwritten by others. + */ + do { + id = manager->next_id++; + } while (g_hash_table_lookup (manager->handler_table, (gpointer)(long)id)); + + g_hash_table_insert (manager->handler_table, (gpointer)(long)id, h); + + return id; +} + +void cevent_manager_unregister (CEventManager *manager, uint32_t id) +{ + g_hash_table_remove (manager->handler_table, (gpointer)(long)id); +} + +void +cevent_manager_add_event (CEventManager *manager, uint32_t id, + void *data) +{ + pthread_mutex_lock (&manager->mutex); + + struct CEvent cevent; + char *buf = (char *) &cevent; + + cevent.id = id; + cevent.data = data; + if (ccnet_util_pipewriten(manager->pipefd[1], buf, CEVENT_SIZE) != CEVENT_SIZE) { + g_warning ("add event error\n"); + } + + pthread_mutex_unlock (&manager->mutex); +} diff --git a/lib/client-pool.c b/lib/client-pool.c new file mode 100644 index 0000000..4b0b423 --- /dev/null +++ b/lib/client-pool.c @@ -0,0 +1,63 @@ +#include "include.h" + +#include +#include + +#include +#include + +struct CcnetClientPool { + GQueue *clients; + pthread_mutex_t lock; + const char *central_config_dir; + const char *conf_dir; +}; + +struct CcnetClientPool * +ccnet_client_pool_new (const char *central_config_dir, const char *conf_dir) +{ + CcnetClientPool *pool = g_new0 (CcnetClientPool, 1); + + pool->clients = g_queue_new (); + pthread_mutex_init (&pool->lock, NULL); + pool->conf_dir = g_strdup(conf_dir); + pool->central_config_dir = g_strdup(central_config_dir); + + return pool; +} + +CcnetClient * +ccnet_client_pool_get_client (struct CcnetClientPool *cpool) +{ + CcnetClient *client; + + pthread_mutex_lock (&cpool->lock); + client = g_queue_pop_head (cpool->clients); + pthread_mutex_unlock (&cpool->lock); + + if (!client) { + client = ccnet_client_new (); + if (ccnet_client_load_confdir (client, cpool->central_config_dir, cpool->conf_dir) < 0) { + g_warning ("[client pool] Failed to load conf dir.\n"); + g_object_unref (client); + return NULL; + } + if (ccnet_client_connect_daemon (client, CCNET_CLIENT_SYNC) < 0) { + g_warning ("[client pool] Failed to connect.\n"); + g_object_unref (client); + return NULL; + } + } + + return client; +} + +void +ccnet_client_pool_return_client (struct CcnetClientPool *cpool, + CcnetClient *client) +{ + pthread_mutex_lock (&cpool->lock); + g_queue_push_tail (cpool->clients, client); + pthread_mutex_unlock (&cpool->lock); +} + diff --git a/lib/db.c b/lib/db.c new file mode 100644 index 0000000..50f1898 --- /dev/null +++ b/lib/db.c @@ -0,0 +1,223 @@ + +#include +#include + +#include "db.h" + +int +sqlite_open_db (const char *db_path, sqlite3 **db) +{ + int result; + const char *errmsg; + + result = sqlite3_open (db_path, db); + if (result) { + errmsg = sqlite3_errmsg (*db); + + g_warning ("Couldn't open database:'%s', %s\n", + db_path, errmsg ? errmsg : "no error given"); + + sqlite3_close (*db); + return -1; + } + + return 0; +} + +int sqlite_close_db (sqlite3 *db) +{ + return sqlite3_close (db); +} + +sqlite3_stmt * +sqlite_query_prepare (sqlite3 *db, const char *sql) +{ + sqlite3_stmt *stmt; + int result; + + result = sqlite3_prepare_v2 (db, sql, -1, &stmt, NULL); + + if (result != SQLITE_OK) { + const gchar *str = sqlite3_errmsg (db); + + g_warning ("Couldn't prepare query, error:%d->'%s'\n\t%s\n", + result, str ? str : "no error given", sql); + + return NULL; + } + + return stmt; +} + +int +sqlite_query_exec (sqlite3 *db, const char *sql) +{ + char *errmsg = NULL; + int result; + + result = sqlite3_exec (db, sql, NULL, NULL, &errmsg); + + if (result != SQLITE_OK) { + if (errmsg != NULL) { + g_warning ("SQL error: %d - %s\n:\t%s\n", result, errmsg, sql); + sqlite3_free (errmsg); + } + return -1; + } + + return 0; +} + +int +sqlite_begin_transaction (sqlite3 *db) +{ + char *sql = "BEGIN TRANSACTION;"; + return sqlite_query_exec (db, sql); +} + +int +sqlite_end_transaction (sqlite3 *db) +{ + char *sql = "END TRANSACTION;"; + return sqlite_query_exec (db, sql); +} + + +gboolean +sqlite_check_for_existence (sqlite3 *db, const char *sql) +{ + sqlite3_stmt *stmt; + int result; + + stmt = sqlite_query_prepare (db, sql); + if (!stmt) + return FALSE; + + result = sqlite3_step (stmt); + if (result == SQLITE_ERROR) { + const gchar *str = sqlite3_errmsg (db); + + g_warning ("Couldn't execute query, error: %d->'%s'\n", + result, str ? str : "no error given"); + sqlite3_finalize (stmt); + return FALSE; + } + sqlite3_finalize (stmt); + + if (result == SQLITE_ROW) + return TRUE; + return FALSE; +} + +int +sqlite_foreach_selected_row (sqlite3 *db, const char *sql, + SqliteRowFunc callback, void *data) +{ + sqlite3_stmt *stmt; + int result; + int n_rows = 0; + + stmt = sqlite_query_prepare (db, sql); + if (!stmt) { + return -1; + } + + while (1) { + result = sqlite3_step (stmt); + if (result != SQLITE_ROW) + break; + n_rows++; + if (!callback (stmt, data)) + break; + } + + if (result == SQLITE_ERROR) { + const gchar *s = sqlite3_errmsg (db); + + g_warning ("Couldn't execute query, error: %d->'%s'\n", + result, s ? s : "no error given"); + sqlite3_finalize (stmt); + return -1; + } + + sqlite3_finalize (stmt); + return n_rows; +} + +int sqlite_get_int (sqlite3 *db, const char *sql) +{ + int ret = -1; + int result; + sqlite3_stmt *stmt; + + if ( !(stmt = sqlite_query_prepare(db, sql)) ) + return 0; + + result = sqlite3_step (stmt); + if (result == SQLITE_ROW) { + ret = sqlite3_column_int (stmt, 0); + sqlite3_finalize (stmt); + return ret; + } + + if (result == SQLITE_ERROR) { + const gchar *str = sqlite3_errmsg (db); + g_warning ("Couldn't prepare query, error: %d->'%s'\n", + result, str ? str : "no error given"); + sqlite3_finalize (stmt); + return 0; + } + return ret; +} + +gint64 sqlite_get_int64 (sqlite3 *db, const char *sql) +{ + gint64 ret = -1; + int result; + sqlite3_stmt *stmt; + + if ( !(stmt = sqlite_query_prepare(db, sql)) ) + return 0; + + result = sqlite3_step (stmt); + if (result == SQLITE_ROW) { + ret = sqlite3_column_int64 (stmt, 0); + sqlite3_finalize (stmt); + return ret; + } + + if (result == SQLITE_ERROR) { + const gchar *str = sqlite3_errmsg (db); + g_warning ("Couldn't prepare query, error: %d->'%s'\n", + result, str ? str : "no error given"); + sqlite3_finalize (stmt); + return 0; + } + return ret; +} + +char *sqlite_get_string (sqlite3 *db, const char *sql) +{ + const char *res = NULL; + int result; + sqlite3_stmt *stmt; + + if ( !(stmt = sqlite_query_prepare(db, sql)) ) + return NULL; + + result = sqlite3_step (stmt); + if (result == SQLITE_ROW) { + res = (const char *)sqlite3_column_text (stmt, 0); + sqlite3_finalize (stmt); + return g_strdup(res); + } + + if (result == SQLITE_ERROR) { + const gchar *str = sqlite3_errmsg (db); + g_warning ("Couldn't prepare query, error: %d->'%s'\n", + result, str ? str : "no error given"); + sqlite3_finalize (stmt); + return NULL; + } + return NULL; +} diff --git a/lib/db.h b/lib/db.h new file mode 100644 index 0000000..86f5041 --- /dev/null +++ b/lib/db.h @@ -0,0 +1,33 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef DB_UTILS_H +#define DB_UTILS_H + +#include + +int sqlite_open_db (const char *db_path, sqlite3 **db); + +int sqlite_close_db (sqlite3 *db); + +sqlite3_stmt *sqlite_query_prepare (sqlite3 *db, const char *sql); + +int sqlite_query_exec (sqlite3 *db, const char *sql); +int sqlite_begin_transaction (sqlite3 *db); +int sqlite_end_transaction (sqlite3 *db); + +gboolean sqlite_check_for_existence (sqlite3 *db, const char *sql); + +typedef gboolean (*SqliteRowFunc) (sqlite3_stmt *stmt, void *data); + +int +sqlite_foreach_selected_row (sqlite3 *db, const char *sql, + SqliteRowFunc callback, void *data); + +int sqlite_get_int (sqlite3 *db, const char *sql); + +gint64 sqlite_get_int64 (sqlite3 *db, const char *sql); + +char *sqlite_get_string (sqlite3 *db, const char *sql); + + +#endif diff --git a/lib/htree.c b/lib/htree.c new file mode 100644 index 0000000..7ee34be --- /dev/null +++ b/lib/htree.c @@ -0,0 +1,295 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include +#include +#include +#include + +#include "htree.h" + +#define MAX_HEIGHT 7 +#define INDEX(hashid,depth) (depth%2 ? (hashid[depth/2] && 0x0f):(hashid[depth/2] >> 4)) +#define IS_NODE(n) (n->is_node) + +static const int g_index[] = {0, 1, 17, 273, 4369, 69905, 1118481, 17895697, 286331153}; + +static inline void hashxor(char *hashid, char *id, int len) +{ + int i = 0; + for (i = 0; i < len; ++i) { + hashid[i] = hashid[i] ^ id[i]; + } +} + +static inline int get_right_height (int size) +{ + int height = 0; + while (g_index[height+1] < size) + ++height; + if (height < 1)return 1; + return height; +} + +static inline unsigned int get_pos (HTree *tree, HTNode *node) +{ + return (node - tree->nodes) - g_index[(int)node->depth]; +} + +static inline void ht_set_data (HTree *tree, HTNode *node, HTData *data) +{ + tree->datas[node - tree->nodes] = data; +} + +static inline HTItem *create_item (unsigned char *hashid, void *data) +{ + HTItem *it = malloc (sizeof (HTItem)); + it->hashid = hashid; + it->data = data; + it->next = NULL; +} + +static inline void delete_item (HTree *tree, HTItem *it) +{ + if (tree->item_free) + tree->item_free (it); + free (it); +} + + +static void *load_node (HTree *tree, HTNode *node) +{ + HTData *data = ht_get_data (tree, node); + if (data) return data; + + data = malloc (sizeof(HTData)); + data->size = 0; + data->hashid = malloc (tree->hashid_len); + memset (data->hashid, 0, tree->hashid_len); + data->item_list = NULL; + ht_set_data (tree, node, data); + return data; +} + +static void delete_data (HTree *tree, HTData *data) +{ + HTItem *p = NULL;; + if (data) { + if (data->hashid) + free (data->hashid); + while (data->item_list) { + p = data->item_list; + data->item_list = p->next; + delete_item (tree, p); + } + free(data); + } +} + +static int add_item (HTree *tree, HTNode *node, unsigned char *hashid, void *data) +{ + if (node == NULL) + return 0; + int r = 0, i = 0; + HTData *htdata = NULL; + if (IS_NODE(node)) { + r = add_item (tree, + ht_get_child (tree, node, INDEX(hashid, node->depth)), + hashid, + data); + if (r) { + htdata = load_node(tree, node); + hashxor (htdata->hashid, hashid, tree->hashid_len); + } + return r; + } + + htdata = load_node(tree, node); + HTItem *p = htdata->item_list; + for (i = 0; i < htdata->size; i++){ + if (memcmp (hashid, p->hashid, tree->hashid_len) == 0) { + return 0; + } + p = p->next; + } + HTItem *it = create_item (hashid, data); + it->next = htdata->item_list; + htdata->item_list = it; + ++htdata->size; + hashxor (htdata->hashid, it->hashid, tree->hashid_len); + return 1; +} + +static int remove_item (HTree *tree, HTNode *node, unsigned char *hashid) +{ + if (node == NULL) + return 0; + int r = 0, i = 0; + HTData *data = NULL; + if (IS_NODE(node)) { + r = remove_item (tree, + ht_get_child (tree, node, INDEX(hashid, node->depth)), + hashid); + if (r) { + data = load_node(tree, node); + hashxor (data->hashid, hashid, tree->hashid_len); + } + return r; + } + + data = load_node(tree, node); + HTItem *p = data->item_list; + HTItem *pre = NULL; + for (i = 0; i < data->size; i++){ + if (memcmp (hashid, p->hashid, tree->hashid_len) == 0) { + if (pre == NULL) { + data->item_list = p->next; + } else { + pre->next = p->next; + } + --data->size; + hashxor (data->hashid, hashid, tree->hashid_len); + delete_item (tree, p); + return 1; + } + pre = p; + p = p->next; + } + + return 0; +} + +HTree* ht_new (int size, int hashlen) +{ + HTree *ht = (HTree *)malloc (sizeof(HTree)); + int height = get_right_height (size); + int i = 0, j = 0; + + ht->height = height; + ht->size = g_index[height]; + ht->hashid_len = hashlen; + ht->item_free = NULL; + ht->nodes = malloc (ht->size *sizeof(HTNode)); + ht->datas = malloc (ht->size *sizeof(HTData *)); + memset (ht->nodes, 0, ht->size *sizeof(HTNode)); + memset (ht->datas, 0, ht->size *sizeof(HTData *)); + + for (i = 0; i < height; ++i) { + for (j = g_index[i]; j < g_index[i+1]; ++j) { + (ht->nodes[j]).is_node = 1; + (ht->nodes[j]).depth = i; + } + } + for (j = g_index[height-1]; j < g_index[height]; ++j) { + (ht->nodes[j]).is_node = 0; + } + +} + +int ht_add (HTree *tree, unsigned char *hashid, void *data) +{ + return add_item (tree, tree->nodes, hashid, data); +} + +int ht_remove (HTree *tree, unsigned char *hashid) +{ + return remove_item (tree, tree->nodes, hashid); +} + +void ht_resize (HTree *ht, int size) +{ + int height = get_right_height (size); + if (height <= ht->height) { + return; + } + /* TODO resize the hash tree */ +} + +void ht_clear (HTree *tree) +{ + assert (tree); + + int i; + for(i = 0; i < tree->size; i++){ + delete_data (tree, tree->datas[i]); + } + memset (tree->datas, 0, sizeof(HTData*) * tree->size); + free (tree->nodes); + free (tree->datas); + free (tree); +} + +HTData* ht_get_data (HTree *tree, HTNode *node) +{ + return tree->datas[node - tree->nodes]; +} + +unsigned char* ht_get_node_hash (HTree *tree, HTNode *node) +{ + if (tree->datas[node - tree->nodes] == NULL) + return NULL; + return (tree->datas[node - tree->nodes])->hashid; +} + +HTNode *ht_get_child (HTree *tree, HTNode *node, int b) +{ + assert(0 <= b && b <= 0x0f); + assert(node->depth < tree->height - 1); + + int i = g_index[node->depth + 1] + (get_pos(tree, node) << 4) + b; + + if (i >= tree->size){ + fprintf(stderr, "get_child out of bound: %dth %d >= %d\n", b, i, tree->size); + return NULL; + } + return tree->nodes + i; +} + +HTNode *ht_get_parent (HTree *tree, HTNode *node) +{ + if (node->depth == 0) + return NULL; + int i = g_index[node->depth - 1] + (get_pos(tree, node) >> 4) ; + return tree->nodes + i; +} + +HTNode *ht_get_brother (HTree *tree, HTNode *node) +{ + if (get_pos(tree, node) & 0xff < 15) + return node + 1; + return NULL; +} + +static void remove_node (HTree *tree, HTNode *node) +{ + HTData* data = ht_get_data (tree, node); + if (data == NULL) + return; + + if (HTNODE_IS_LEAF(node)) { + delete_data (tree, data); + } else { + int i = 0; + HTNode *child = NULL; + for (i = 0; i < 16; ++i) { + child = ht_get_child (tree, node, i); + remove_node (tree, child); + } + } +} + +void ht_remove_node (HTree *tree, HTNode *node) +{ + HTData *data = ht_get_data (tree, node); + if (data == NULL) + return; + char *hash = data->hashid; + HTNode *parent = ht_get_parent (tree, node); + + while (parent) { + char *phash = ht_get_node_hash (tree, parent); + hashxor (phash, hash, tree->hashid_len); + parent = ht_get_parent (tree, parent); + } + remove_node (tree, node); + ht_set_data (tree, node, NULL); +} diff --git a/lib/htree.h b/lib/htree.h new file mode 100644 index 0000000..2e011f4 --- /dev/null +++ b/lib/htree.h @@ -0,0 +1,76 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +#ifndef __HTREE_H__ +#define __HTREE_H__ + +typedef struct hash_tree HTree; +typedef void (*ItemFreeFunc)(void *); + +struct ht_item { + unsigned char *hashid; + void *data; + struct ht_item *next; +}; + +struct ht_data { + int size; + unsigned char *hashid; + struct ht_item *item_list; +}; + +struct ht_node { + unsigned char is_node; + unsigned char depth; /* from 0 */ +}; + +struct hash_tree { + int height; + int hashid_len; + int item_data_len; + struct ht_node *nodes; /* an array of ht_node */ + struct ht_data **datas; /* an array of the pointer of ht_data*/ + int size; + ItemFreeFunc item_free; +}; + +typedef struct ht_item HTItem; +typedef struct ht_data HTData; +typedef struct ht_node HTNode; + +#define HTNODE_IS_LEAF(n) (!(((HTNode *)n)->is_node)) +#define HTNODE_IS_NULL(tree, n) (ht_get_data(tree, n) == NULL) + +HTree* ht_new (int size, int hashlen); +void ht_clear (HTree *tree); +int ht_add (HTree *tree, unsigned char *hashid, void *data); +int ht_remove (HTree *tree, unsigned char *hashid); +void ht_resize (HTree *ht, int size); +HTData* ht_get_data (HTree *tree, HTNode *node); +unsigned char* ht_get_node_hash (HTree *tree, HTNode *node); +HTNode *ht_get_parent (HTree *tree, HTNode *node); +HTNode *ht_get_child (HTree *tree, HTNode *node, int b); +HTNode *ht_get_brother (HTree *tree, HTNode *node); +void ht_remove_node (HTree *tree, HTNode *node); + +static inline void ht_set_free_func (HTree *tree, ItemFreeFunc item_free) +{ + tree->item_free = item_free; +} + +static inline HTNode *ht_get_root (HTree *tree) +{ + return tree->nodes; +} + +static inline int ht_get_node_seq (HTree *tree, HTNode *node) +{ + return node - tree->nodes; +} + +static inline HTNode *ht_get_node_by_seq (HTree *tree, int seq) +{ + return tree->nodes + seq; +} + + + +#endif /* __HTREE_H__ */ diff --git a/lib/include.h b/lib/include.h new file mode 100644 index 0000000..7d4893b --- /dev/null +++ b/lib/include.h @@ -0,0 +1,35 @@ + +#include +#include +#include +#include +#include +#include + +#include + +#include "libccnet_utils.h" +#include + +#ifndef ccnet_warning + #define ccnet_warning(fmt, ...) g_warning( "%s: " fmt, __func__ , ##__VA_ARGS__) +#endif + +#ifndef ccnet_error + #define ccnet_error(fmt, ...) g_error( "%s: " fmt, __func__ , ##__VA_ARGS__) +#endif + +#ifndef ccnet_message + #define ccnet_message(fmt, ...) g_message(fmt, ##__VA_ARGS__) +#endif + +#ifndef ccnet_debug + #define ccnet_debug(fmt, ...) g_debug(fmt, ##__VA_ARGS__) +#endif + +#define CCNET_DOMAIN g_quark_from_string("ccnet") + +#ifndef ENABLE_DEBUG +#undef g_debug +#define g_debug(...) +#endif diff --git a/lib/invoke-service-proc.c b/lib/invoke-service-proc.c new file mode 100644 index 0000000..c0074cd --- /dev/null +++ b/lib/invoke-service-proc.c @@ -0,0 +1,76 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "include.h" + +#include "ccnet-client.h" + +#include "peer.h" +#include "processor.h" +#include "invoke-service-proc.h" + +static int invoke_service_start (CcnetProcessor *processor, + int argc, char **argv); +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_SERVICE_PROC, CcnetServiceProcPriv)) + +G_DEFINE_TYPE (CcnetServiceProc, ccnet_service_proc, CCNET_TYPE_PROCESSOR) + +static void +ccnet_service_proc_class_init (CcnetServiceProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->start = invoke_service_start; + proc_class->handle_response = handle_response; +} + +static void +ccnet_service_proc_init (CcnetServiceProc *processor) +{ + /* CCNET_PROCESSOR (processor)->state = INIT; */ +} + +static int invoke_service_start (CcnetProcessor *processor, + int argc, char **argv) +{ + char *cmd; + + if (argc == 0) { + g_warning ("Invalid argument for service-proc\n"); + ccnet_processor_done (processor, FALSE); + } + + cmd = ccnet_util_strjoin_n (" ", argc, argv); + + ccnet_client_send_request (processor->session, + REQUEST_ID (processor->id), cmd); + + return 0; +} + +void +ccnet_service_proc_set_response_cb (CcnetServiceProc *proc, + ResponseCB resp_cb, void *data) +{ + proc->resp_cb = resp_cb; + proc->cb_data = data; +} + + +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + CcnetServiceProc *proc = (CcnetServiceProc *) processor; + + if (proc->resp_cb) + proc->resp_cb (code, code_msg, content, clen, proc->cb_data); + + if (code[0] == '4' || code[0] == '5') + ccnet_processor_done (processor, FALSE); +} diff --git a/lib/job-mgr.c b/lib/job-mgr.c new file mode 100644 index 0000000..e38c254 --- /dev/null +++ b/lib/job-mgr.c @@ -0,0 +1,175 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#include +#else +#include +#endif + +#include +#include +#include +#include + +#define MAX_THREADS 50 +#define MAX_IDLE_THREADS 10 + +#ifdef CCNET_LIB + #include "libccnet_utils.h" + #define pipereadn ccnet_util_pipereadn + #define pipewriten ccnet_util_pipewriten + #define pipeclose ccnet_util_pipeclose + #define ccnet_pipe ccnet_util_pipe +#else + #include "utils.h" +#endif + +#include "job-mgr.h" + +struct _CcnetJob { + CcnetJobManager *manager; + + int id; + ccnet_pipe_t pipefd[2]; + + JobThreadFunc thread_func; + JobDoneCallback done_func; /* called when the thread is done */ + void *data; + + /* the done callback should only access this field */ + void *result; +}; + + +void +ccnet_job_manager_remove_job (CcnetJobManager *mgr, int job_id); + +static void +job_thread_wrapper (void *vdata, void *unused) +{ + CcnetJob *job = vdata; + + + job->result = job->thread_func (job->data); + if (pipewriten (job->pipefd[1], "a", 1) != 1) { + g_warning ("[Job Manager] write to pipe error: %s\n", strerror(errno)); + } +} + +static void +job_done_cb (evutil_socket_t fd, short event, void *vdata) +{ + CcnetJob *job = vdata; + char buf[1]; + + if (pipereadn (job->pipefd[0], buf, 1) != 1) { + g_warning ("[Job Manager] read pipe error: %s\n", strerror(errno)); + } + pipeclose (job->pipefd[0]); + pipeclose (job->pipefd[1]); + if (job->done_func) { + job->done_func (job->result); + } + + ccnet_job_manager_remove_job (job->manager, job->id); +} + +int +job_thread_create (CcnetJob *job) +{ + if (ccnet_pipe (job->pipefd) < 0) { + g_warning ("pipe error: %s\n", strerror(errno)); + return -1; + } + + g_thread_pool_push (job->manager->thread_pool, job, NULL); + +#ifndef UNIT_TEST + event_once (job->pipefd[0], EV_READ, job_done_cb, job, NULL); +#endif + + return 0; +} + +CcnetJob * +ccnet_job_new () +{ + CcnetJob *job; + + job = g_new0 (CcnetJob, 1); + return job; +} + +void +ccnet_job_free (CcnetJob *job) +{ + g_free (job); +} + +CcnetJobManager * +ccnet_job_manager_new (int max_threads) +{ + CcnetJobManager *mgr; + + mgr = g_new0 (CcnetJobManager, 1); + mgr->jobs = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, (GDestroyNotify)ccnet_job_free); + mgr->thread_pool = g_thread_pool_new (job_thread_wrapper, + NULL, + max_threads, + FALSE, + NULL); + /* g_thread_pool_set_max_unused_threads (MAX_IDLE_THREADS); */ + + return mgr; +} + +void +ccnet_job_manager_free (CcnetJobManager *mgr) +{ + g_hash_table_destroy (mgr->jobs); + g_thread_pool_free (mgr->thread_pool, TRUE, FALSE); + g_free (mgr); +} + +int +ccnet_job_manager_schedule_job (CcnetJobManager *mgr, + JobThreadFunc func, + JobDoneCallback done_func, + void *data) +{ + CcnetJob *job = ccnet_job_new (); + job->id = mgr->next_job_id++; + job->manager = mgr; + job->thread_func = func; + job->done_func = done_func; + job->data = data; + + g_hash_table_insert (mgr->jobs, (gpointer)(long)job->id, job); + + if (job_thread_create (job) < 0) { + g_hash_table_remove (mgr->jobs, (gpointer)(long)job->id); + return -1; + } + + return job->id; +} + +void +ccnet_job_manager_remove_job (CcnetJobManager *mgr, int job_id) +{ + g_hash_table_remove (mgr->jobs, (gpointer)(long)job_id); +} + +#ifdef UNIT_TEST +void +ccnet_job_manager_wait_job (CcnetJobManager *mgr, int job_id) +{ + CcnetJob *job; + + job = g_hash_table_lookup (mgr->jobs, (gpointer)(long)job_id); + /* manually call job_done_cb */ + job_done_cb (0, 0, (void *)job); +} +#endif diff --git a/lib/libccnet_utils.c b/lib/libccnet_utils.c new file mode 100644 index 0000000..3a24be6 --- /dev/null +++ b/lib/libccnet_utils.c @@ -0,0 +1,882 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include + +#include "libccnet_utils.h" + +#ifdef WIN32 + #include + #include + #include + #include + #include +#else + #include +#endif + +#ifndef WIN32 +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +#ifdef WIN32 +int +ccnet_util_pgpipe (ccnet_pipe_t handles[2]) +{ + SOCKET s; + struct sockaddr_in serv_addr; + int len = sizeof( serv_addr ); + + handles[0] = handles[1] = INVALID_SOCKET; + + if ( ( s = socket( AF_INET, SOCK_STREAM, 0 ) ) == INVALID_SOCKET ) + { + g_warning("pgpipe failed to create socket: %d\n", WSAGetLastError()); + return -1; + } + + memset( &serv_addr, 0, sizeof( serv_addr ) ); + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(0); + serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + if (bind(s, (SOCKADDR *) & serv_addr, len) == SOCKET_ERROR) + { + g_warning("pgpipe failed to bind: %d\n", WSAGetLastError()); + closesocket(s); + return -1; + } + if (listen(s, 1) == SOCKET_ERROR) + { + g_warning("pgpipe failed to listen: %d\n", WSAGetLastError()); + closesocket(s); + return -1; + } + if (getsockname(s, (SOCKADDR *) & serv_addr, &len) == SOCKET_ERROR) + { + g_warning("pgpipe failed to getsockname: %d\n", WSAGetLastError()); + closesocket(s); + return -1; + } + if ((handles[1] = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) + { + g_warning("pgpipe failed to create socket 2: %d\n", WSAGetLastError()); + closesocket(s); + return -1; + } + + if (connect(handles[1], (SOCKADDR *) & serv_addr, len) == SOCKET_ERROR) + { + g_warning("pgpipe failed to connect socket: %d\n", WSAGetLastError()); + closesocket(s); + return -1; + } + if ((handles[0] = accept(s, (SOCKADDR *) & serv_addr, &len)) == INVALID_SOCKET) + { + g_warning("pgpipe failed to accept socket: %d\n", WSAGetLastError()); + closesocket(handles[1]); + handles[1] = INVALID_SOCKET; + closesocket(s); + return -1; + } + closesocket(s); + return 0; +} +#endif + +struct timeval +ccnet_util_timeval_from_msec (uint64_t milliseconds) +{ + struct timeval ret; + const uint64_t microseconds = milliseconds * 1000; + ret.tv_sec = microseconds / 1000000; + ret.tv_usec = microseconds % 1000000; + return ret; +} + +int +ccnet_util_checkdir (const char *dir) +{ + struct stat st; + +#ifdef WIN32 + /* remove trailing '\\' */ + char *path = g_strdup(dir); + char *p = (char *)path + strlen(path) - 1; + while (*p == '\\' || *p == '/') *p-- = '\0'; + if ((g_stat(dir, &st) < 0) || !S_ISDIR(st.st_mode)) { + g_free (path); + return -1; + } + g_free (path); + return 0; +#else + if ((g_stat(dir, &st) < 0) || !S_ISDIR(st.st_mode)) + return -1; + return 0; +#endif +} + +int +ccnet_util_checkdir_with_mkdir (const char *dir) +{ +#ifdef WIN32 + int ret; + char *path = g_strdup(dir); + char *p = (char *)path + strlen(path) - 1; + while (*p == '\\' || *p == '/') *p-- = '\0'; + ret = g_mkdir_with_parents(path, 0755); + g_free (path); + return ret; +#else + return g_mkdir_with_parents(dir, 0755); +#endif +} + + +ssize_t /* Read "n" bytes from a descriptor. */ +ccnet_util_recvn(evutil_socket_t fd, void *vptr, size_t n) +{ + size_t nleft; + ssize_t nread; + char *ptr; + + ptr = vptr; + nleft = n; + while (nleft > 0) { +#ifndef WIN32 + if ( (nread = read(fd, ptr, nleft)) < 0) +#else + if ( (nread = recv(fd, ptr, nleft, 0)) < 0) +#endif + { + if (errno == EINTR) + nread = 0; /* and call read() again */ + else + return(-1); + } else if (nread == 0) + break; /* EOF */ + + nleft -= nread; + ptr += nread; + } + return(n - nleft); /* return >= 0 */ +} + +ssize_t /* Write "n" bytes to a descriptor. */ +ccnet_util_sendn (evutil_socket_t fd, const void *vptr, size_t n) +{ + size_t nleft; + ssize_t nwritten; + const char *ptr; + + ptr = vptr; + nleft = n; + while (nleft > 0) { +#ifndef WIN32 + if ( (nwritten = write(fd, ptr, nleft)) <= 0) +#else + if ( (nwritten = send(fd, ptr, nleft, 0)) <= 0) +#endif + { + if (nwritten < 0 && errno == EINTR) + nwritten = 0; /* and call write() again */ + else + return(-1); /* error */ + } + + nleft -= nwritten; + ptr += nwritten; + } + return(n); +} + +char* +ccnet_util_expand_path (const char *src) +{ +#ifdef WIN32 + char new_path[PATH_MAX + 1]; + char *p = new_path; + const char *q = src; + + memset(new_path, 0, sizeof(new_path)); + if (*src == '~') { + const char *home = g_get_home_dir(); + memcpy(new_path, home, strlen(home)); + p += strlen(new_path); + q++; + } + memcpy(p, q, strlen(q)); + + /* delete the charactor '\' or '/' at the end of the path + * because the function stat faied to deal with directory names + * with '\' or '/' in the end */ + p = new_path + strlen(new_path) - 1; + while(*p == '\\' || *p == '/') *p-- = '\0'; + + return strdup (new_path); +#else + const char *next_in, *ntoken; + char new_path[PATH_MAX + 1]; + char *next_out; + int len; + + /* special cases */ + if (!src || *src == '\0') + return NULL; + if (strlen(src) > PATH_MAX) + return NULL; + + next_in = src; + next_out = new_path; + *next_out = '\0'; + + if (*src == '~') { + /* handle src start with '~' or '~' like '~plt' */ + struct passwd *pw = NULL; + + for ( ; *next_in != '/' && *next_in != '\0'; next_in++) ; + + len = next_in - src; + if (len == 1) { + pw = getpwuid (geteuid()); + } else { + /* copy '~' to new_path */ + memcpy (new_path, src, len); + new_path[len] = '\0'; + pw = getpwnam (new_path + 1); + } + if (pw == NULL) + return NULL; + + len = strlen (pw->pw_dir); + memcpy (new_path, pw->pw_dir, len); + next_out = new_path + len; + *next_out = '\0'; + + if (*next_in == '\0') + return strdup (new_path); + } else if (*src != '/') { + getcwd (new_path, PATH_MAX); + for ( ; *next_out; next_out++) ; /* to '\0' */ + } + + while (*next_in != '\0') { + /* move ntoken to the next not '/' char */ + for (ntoken = next_in; *ntoken == '/'; ntoken++) ; + + for (next_in = ntoken; *next_in != '/' + && *next_in != '\0'; next_in++) ; + + len = next_in - ntoken; + + if (len == 0) { + /* the path ends with '/', keep it */ + *next_out++ = '/'; + *next_out = '\0'; + break; + } + + if (len == 2 && ntoken[0] == '.' && ntoken[1] == '.') + { + /* '..' */ + for (; next_out > new_path && *next_out != '/'; next_out--) + ; + *next_out = '\0'; + } else if (ntoken[0] != '.' || len != 1) { + /* not '.' */ + *next_out++ = '/'; + memcpy (next_out, ntoken, len); + next_out += len; + *next_out = '\0'; + } + } + + /* the final special case */ + if (new_path[0] == '\0') { + new_path[0] = '/'; + new_path[1] = '\0'; + } + return strdup (new_path); +#endif +} + +#ifndef WIN32 +char* ccnet_util_gen_uuid () +{ + char *uuid_str = g_malloc (37); + uuid_t uuid; + + uuid_generate (uuid); + uuid_unparse_lower (uuid, uuid_str); + + return uuid_str; +} + +#else +char* ccnet_util_gen_uuid () +{ + char *uuid_str = g_malloc (37); + unsigned char *str = NULL; + UUID uuid; + + UuidCreate(&uuid); + UuidToString(&uuid, &str); + memcpy(uuid_str, str, 37); + RpcStringFree(&str); + return uuid_str; +} +#endif + +char* ccnet_util_strjoin_n (const char *seperator, int argc, char **argv) +{ + GString *buf; + int i; + char *str; + + if (argc == 0) + return NULL; + + buf = g_string_new (argv[0]); + for (i = 1; i < argc; ++i) { + g_string_append (buf, seperator); + g_string_append (buf, argv[i]); + } + + str = buf->str; + g_string_free (buf, FALSE); + return str; +} + +/** + * handle the empty string problem. + */ +gchar* +ccnet_util_key_file_get_string (GKeyFile *keyf, + const char *category, + const char *key) +{ + gchar *v; + + if (!g_key_file_has_key (keyf, category, key, NULL)) + return NULL; + + v = g_key_file_get_string (keyf, category, key, NULL); + if (v != NULL && v[0] == '\0') { + g_free(v); + return NULL; + } + + return v; +} + +void +ccnet_util_string_list_free (GList *str_list) +{ + GList *ptr = str_list; + + while (ptr) { + g_free (ptr->data); + ptr = ptr->next; + } + + g_list_free (str_list); +} + +void +ccnet_util_string_list_join (GList *str_list, GString *str, const char *seperator) +{ + GList *ptr; + if (!str_list) + return; + + ptr = str_list; + g_string_append (str, ptr->data); + + for (ptr = ptr->next; ptr; ptr = ptr->next) { + g_string_append (str, seperator); + g_string_append (str, (char *)ptr->data); + } +} + +static GList * +string_list_parse (const char *list_in_str, const char *seperator) +{ + if (!list_in_str) + return NULL; + + GList *list = NULL; + char **array = g_strsplit (list_in_str, seperator, 0); + char **ptr; + + for (ptr = array; *ptr; ptr++) { + list = g_list_prepend (list, g_strdup(*ptr)); + } + list = g_list_reverse (list); + + g_strfreev (array); + return list; +} + +GList * +ccnet_util_string_list_parse_sorted (const char *list_in_str, const char *seperator) +{ + GList *list = string_list_parse (list_in_str, seperator); + + return g_list_sort (list, (GCompareFunc)g_strcmp0); +} + +static unsigned hexval(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return ~0; +} + +int +ccnet_util_hex_to_rawdata (const char *hex_str, + unsigned char *rawdata, + int n_bytes) +{ + int i; + for (i = 0; i < n_bytes; i++) { + unsigned int val = (hexval(hex_str[0]) << 4) | hexval(hex_str[1]); + if (val & ~0xff) + return -1; + *rawdata++ = val; + hex_str += 2; + } + return 0; +} + + +#ifdef WIN32 + +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif + +#ifndef IN6ADDRSZ +#define IN6ADDRSZ 16 +#endif + +#ifndef INT16SZ +#define INT16SZ 2 +#endif + +#ifndef INADDRSZ +#define INADDRSZ 4 +#endif + +#ifndef inet_ntop +static const char * +inet_ntop4 (const u_char *src, char *dst, size_t size) +{ + static const char fmt[] = "%u.%u.%u.%u"; + char tmp[sizeof("255.255.255.255")]; + int l; + l = _snprintf(tmp, size, fmt, src[0], src[1], src[2], src[3]); + if (l <= 0 || l >= size) { + return (NULL); + } + strncpy(dst, tmp, size); + return (dst); +} + +static const char * +inet_ntop6 (const u_char *src, char *dst, size_t size) +{ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"]; + char *tp, *ep; + struct + { + int base, len; + } best, cur; + u_int words[IN6ADDRSZ / INT16SZ]; + int i; + int advance; + memset(words, '\0', sizeof(words)); + for (i = 0; i < IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + cur.base = -1; + best.len = -1; + cur.len = -1; + for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } + else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + tp = tmp; + ep = tmp + sizeof(tmp); + for (i = 0; i < (IN6ADDRSZ / INT16SZ) && tp < ep; i++) { +/** Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) { + if (i == best.base) { + if (tp + 1 >= ep) + return (NULL); + *tp++ = ':'; + } + continue; + } +/** Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) { + if (tp + 1 >= ep) + return (NULL); + *tp++ = ':'; + } +/** Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && + (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { + if (!inet_ntop4(src+12, tp, (size_t)(ep - tp))) + return (NULL); + tp += strlen(tp); + break; + } + advance = snprintf(tp, ep - tp, "%x", words[i]); + if (advance <= 0 || advance >= ep - tp) + return (NULL); + tp += advance; + } +/** Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ)) { + if (tp + 1 >= ep) + return (NULL); + *tp++ = ':'; + } + if (tp + 1 >= ep) + return (NULL); + *tp++ = '\0'; + +/** + * Check for overflow, copy, and we're done. + */ + if ((size_t)(tp - tmp) > size) { + errno = ENOSPC; + return (NULL); + } + strncpy(dst, tmp, size); + dst[size] = '\0'; + return (dst); +} + +const char * +ccnet_util_inet_ntop(int af, const void *src, char *dst, size_t size) +{ + switch (af) { + case AF_INET: + return (inet_ntop4(src, dst, size)); + case AF_INET6: + return (inet_ntop6(src, dst, size)); + default: + return (NULL); + } +/** NOTREACHED */ +} +#endif //inet_ntop + +#ifndef inet_aton +int +ccnet_util_inet_aton (const char *string, struct in_addr *addr) +{ + addr->s_addr = inet_addr(string); + if (addr->s_addr != -1 || strcmp("255.255.255.255", string) == 0) + return 1; + return 0; +} +#endif + +#ifndef inet_pton +/* + * Don't even consider trying to compile this on a system where + * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. + */ + +/* int + * inet_pton4(src, dst, pton) + * when last arg is 0: inet_aton(). with hexadecimal, octal and shorthand. + * when last arg is 1: inet_pton(). decimal dotted-quad only. + * return: + * 1 if `src' is a valid input, else 0. + * notice: + * does not touch `dst' unless it's returning 1. + * author: + * Paul Vixie, 1996. + */ +static int +inet_pton4 (const char *src, u_char *dst, int pton) +{ + u_int val; + u_int digit; + int base, n; + unsigned char c; + u_int parts[4]; + register u_int *pp = parts; + + c = *src; + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, isdigit=decimal. + */ + if (!isdigit(c)) + return (0); + val = 0; base = 10; + if (c == '0') { + c = *++src; + if (c == 'x' || c == 'X') + base = 16, c = *++src; + else if (isdigit(c) && c != '9') + base = 8; + } + /* inet_pton() takes decimal only */ + if (pton && base != 10) + return (0); + for (;;) { + if (isdigit(c)) { + digit = c - '0'; + if (digit >= base) + break; + val = (val * base) + digit; + c = *++src; + } else if (base == 16 && isxdigit(c)) { + digit = c + 10 - (islower(c) ? 'a' : 'A'); + if (digit >= 16) + break; + val = (val << 4) | digit; + c = *++src; + } else + break; + } + if (c == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16 bits) + * a.b (with b treated as 24 bits) + * a (with a treated as 32 bits) + */ + if (pp >= parts + 3) + return (0); + *pp++ = val; + c = *++src; + } else + break; + } + /* + * Check for trailing characters. + */ + if (c != '\0' && !isspace(c)) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + /* inet_pton() takes dotted-quad only. it does not take shorthand. */ + if (pton && n != 4) + return (0); + switch (n) { + + case 0: + return (0); /* initial nondigit */ + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (parts[0] > 0xff || val > 0xffffff) + return (0); + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if ((parts[0] | parts[1]) > 0xff || val > 0xffff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if ((parts[0] | parts[1] | parts[2] | val) > 0xff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (dst) { + val = htonl(val); + memcpy(dst, &val, INADDRSZ); + } + return (1); +} + +/* int + * inet_pton6(src, dst) + * convert presentation level address to network order binary form. + * return: + * 1 if `src' is a valid [RFC1884 2.2] address, else 0. + * notice: + * (1) does not touch `dst' unless it's returning 1. + * (2) :: in a full address is silently ignored. + * credit: + * inspired by Mark Andrews. + * author: + * Paul Vixie, 1996. + */ +static int +inet_pton6 (const char *src, u_char *dst) +{ + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + u_char tmp[IN6ADDRSZ], *tp, *endp, *colonp; + const char *xdigits, *curtok; + int ch, saw_xdigit; + u_int val; + + memset((tp = tmp), '\0', IN6ADDRSZ); + endp = tp + IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + return (0); + curtok = src; + saw_xdigit = 0; + val = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) + pch = strchr((xdigits = xdigits_u), ch); + if (pch != NULL) { + val <<= 4; + val |= (pch - xdigits); + if (val > 0xffff) + return (0); + saw_xdigit = 1; + continue; + } + if (ch == ':') { + curtok = src; + if (!saw_xdigit) { + if (colonp) + return (0); + colonp = tp; + continue; + } else if (*src == '\0') + return (0); + if (tp + INT16SZ > endp) + return (0); + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + saw_xdigit = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + INADDRSZ) <= endp) && + inet_pton4(curtok, tp, 1) > 0) { + tp += INADDRSZ; + saw_xdigit = 0; + break; /* '\0' was seen by inet_pton4(). */ + } + return (0); + } + if (saw_xdigit) { + if (tp + INT16SZ > endp) + return (0); + *tp++ = (u_char) (val >> 8) & 0xff; + *tp++ = (u_char) val & 0xff; + } + if (colonp != NULL) { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const int n = tp - colonp; + int i; + + if (tp == endp) + return (0); + for (i = 1; i <= n; i++) { + endp[- i] = colonp[n - i]; + colonp[n - i] = 0; + } + tp = endp; + } + if (tp != endp) + return (0); + memcpy(dst, tmp, IN6ADDRSZ); + return (1); +} + +/* int + * inet_pton(af, src, dst) + * convert from presentation format (which usually means ASCII printable) + * to network format (which is usually some kind of binary format). + * return: + * 1 if the address was valid for the specified address family + * 0 if the address wasn't valid (`dst' is untouched in this case) + * -1 if some other error occurred (`dst' is untouched in this case, too) + * author: + * Paul Vixie, 1996. + */ +int +ccnet_util_inet_pton (int af, const char *src, void *dst) +{ + switch (af) { + case AF_INET: + return (inet_pton4(src, dst, 1)); + case AF_INET6: + return (inet_pton6(src, dst)); + default: + errno = EAFNOSUPPORT; + return (-1); + } + /* NOTREACHED */ +} +#endif + +#endif //WIN32 diff --git a/lib/libccnet_utils.h b/lib/libccnet_utils.h new file mode 100644 index 0000000..475d265 --- /dev/null +++ b/lib/libccnet_utils.h @@ -0,0 +1,89 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +#ifndef LIBCCNET_UTILS_H +#define LIBCCNET_UTILS_H + +/** + * All the helper function names are prefixed with ccnet_util_XXX to avoid + * name conflict. + */ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#else +#include +#endif + +#ifdef WIN32 + #include + #include + + #ifndef WEXITSTATUS + #define WEXITSTATUS(status) (((status) & 0xff00) >> 8) + #endif + + /* Borrowed from libevent */ + #define ccnet_pipe_t intptr_t + + int ccnet_util_pgpipe (ccnet_pipe_t handles[2]); + #define ccnet_util_pipe(a) ccnet_util_pgpipe((a)) + #define ccnet_util_pipeclose(a) closesocket((a)) +#else + #define ccnet_pipe_t int + #define ccnet_util_pipe(a) pipe((a)) + #define ccnet_util_pipeclose(a) close((a)) +#endif + +#define ccnet_util_pipereadn(a,b,c) ccnet_util_recvn((a),(b),(c)) +#define ccnet_util_pipewriten(a,b,c) ccnet_util_sendn((a),(b),(c)) + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +struct timeval ccnet_util_timeval_from_msec (uint64_t milliseconds); + +char* ccnet_util_gen_uuid (); + +/* dir operations */ +int ccnet_util_checkdir (const char *dir); +char* ccnet_util_expand_path (const char *src); + +/* Read "n" bytes from a socket. */ +ssize_t ccnet_util_recvn(evutil_socket_t fd, void *vptr, size_t n); +ssize_t ccnet_util_sendn(evutil_socket_t fd, const void *vptr, size_t n); + +/* string utilities */ + +char * ccnet_util_strjoin_n (const char *seperator, int argc, char **argv); + +void ccnet_util_string_list_free (GList *str_list); +void ccnet_util_string_list_join (GList *str_list, GString *strbuf, const char *seperator); +GList *ccnet_util_string_list_parse_sorted (const char *list_in_str, const char *seperator); + +gchar* ccnet_util_key_file_get_string (GKeyFile *keyf, + const char *category, + const char *key); + +#define ccnet_util_hex_to_sha1(hex, sha1) \ + ccnet_util_hex_to_rawdata((hex), (sha1), 20) + +int +ccnet_util_hex_to_rawdata (const char *hex_str, + unsigned char *rawdata, + int n_bytes); + +#ifdef WIN32 +int ccnet_util_inet_pton(int af, const char *src, void *dst); +#else +#define ccnet_util_inet_pton inet_pton +#endif + +#endif diff --git a/lib/mainloop.c b/lib/mainloop.c new file mode 100644 index 0000000..08c00e8 --- /dev/null +++ b/lib/mainloop.c @@ -0,0 +1,160 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "include.h" +#include + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#include +#include +#else +#include +#endif + +static int +cmdrsp_cb (const char *code, char *content, int clen, void *data) +{ + RegisterServiceCB cb = data; + + if (clen != 0) + ccnet_debug ("Receive cmd response {\n%s}\n", content); + else + ccnet_debug ("Receive cmd response null\n"); + + if (cb) { + if (memcmp (SC_SERV_EXISTED, code, 3) == 0) + cb (FALSE); + else + cb (TRUE); + } + return 0; +} + + +void +ccnet_register_service (CcnetClient *client, + const char *service, + const char *group, + GType proc_type, + RegisterServiceCB cb) +{ + char buf[512]; + g_return_if_fail (group); + + ccnet_proc_factory_register_processor (client->proc_factory, + service, + proc_type); + snprintf (buf, 512, "register-service %s %s", service, group); + ccnet_send_command (client, buf, cmdrsp_cb, cb); +} + +gboolean +ccnet_register_service_sync (CcnetClient *client, + const char *service, + const char *group) +{ + char buf[512]; + GError *error = NULL; + + snprintf (buf, 512, "register-service %s %s", service, group); + ccnet_client_send_cmd (client, buf, &error); + if (error) { + ccnet_warning ("Bad response for register service %s: %d %s", + service, error->code, error->message); + return FALSE; + } + return TRUE; +} + +static void read_cb (evutil_socket_t fd, short event, void *vclient) +{ + CcnetClient *client = vclient; + + if (ccnet_client_read_input (client) <= 0) { + ccnet_client_disconnect_daemon (client); + exit (1); + } +} + + +/** + * Inititialize ccnet client structure, connect daemon and initialize + * event loop. + */ +CcnetClient * +ccnet_init (const char *central_config_dir, const char *confdir) +{ + CcnetClient *client; + + client = ccnet_client_new (); + if ( (ccnet_client_load_confdir(client, central_config_dir, confdir)) < 0 ) { + ccnet_warning ("Read config dir error\n"); + return NULL; + } + + + if (ccnet_client_connect_daemon (client, CCNET_CLIENT_ASYNC) < 0) { + ccnet_warning ("Connect to ccnet daemon error\n"); + exit(1); + } + + ccnet_client_run_synchronizer (client); + + event_init (); + + return client; +} + +void +ccnet_main (CcnetClient *client) +{ + struct event ev; + + event_set (&ev, client->connfd, EV_READ | EV_PERSIST, read_cb, client); + event_add (&ev, NULL); + + event_dispatch (); +} + +void ccnet_send_command (CcnetClient *client, const char *command, + SendcmdProcRcvrspCallback cmd_cb, void *cbdata) +{ + CcnetSendcmdProc *sendcmd_proc = (CcnetSendcmdProc *) + ccnet_proc_factory_create_master_processor (client->proc_factory, + "send-cmd"); + ccnet_sendcmd_proc_set_rcvrsp_cb (sendcmd_proc, cmd_cb, cbdata); + ccnet_processor_start (CCNET_PROCESSOR(sendcmd_proc), 0, NULL); + ccnet_sendcmd_proc_send_command (sendcmd_proc, command); +} + +/* add-peer [--id ] [--addr ] + */ +void ccnet_add_peer (CcnetClient *client, const char *id, const char *addr) +{ + char buf[256]; + if (id == NULL || strlen(id) != 40 || addr == NULL) + return; + + snprintf (buf, 256, "add-peer --id %s --addr %s", id, addr); + ccnet_send_command (client, buf, NULL, NULL); +} + +void ccnet_connect_peer (CcnetClient *client, const char *id) +{ + char buf[256]; + if (id == NULL || strlen(id) != 40) + return; + + snprintf (buf, 256, "connect %s", id); + ccnet_send_command (client, buf, NULL, NULL); +} + +void ccnet_disconnect_peer (CcnetClient *client, const char *id) +{ + char buf[256]; + if (id == NULL || strlen(id) != 40) + return; + + snprintf (buf, 256, "disconnect %s", id); + ccnet_send_command (client, buf, NULL, NULL); +} diff --git a/lib/marshal.c b/lib/marshal.c new file mode 100644 index 0000000..ed9aba5 --- /dev/null +++ b/lib/marshal.c @@ -0,0 +1,86 @@ + +#include + + +#ifdef G_ENABLE_DEBUG +#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) +#define g_marshal_value_peek_char(v) g_value_get_char (v) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */ + + +/* VOID:POINTER,STRING (marshal.list:1) */ +void +g_cclosure_user_marshal_VOID__POINTER_STRING (GClosure *closure, + GValue *return_value G_GNUC_UNUSED, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint G_GNUC_UNUSED, + gpointer marshal_data) +{ + typedef void (*GMarshalFunc_VOID__POINTER_STRING) (gpointer data1, + gpointer arg_1, + gpointer arg_2, + gpointer data2); + register GMarshalFunc_VOID__POINTER_STRING callback; + register GCClosure *cc = (GCClosure*) closure; + register gpointer data1, data2; + + g_return_if_fail (n_param_values == 3); + + if (G_CCLOSURE_SWAP_DATA (closure)) + { + data1 = closure->data; + data2 = g_value_peek_pointer (param_values + 0); + } + else + { + data1 = g_value_peek_pointer (param_values + 0); + data2 = closure->data; + } + callback = (GMarshalFunc_VOID__POINTER_STRING) (marshal_data ? marshal_data : cc->callback); + + callback (data1, + g_marshal_value_peek_pointer (param_values + 1), + g_marshal_value_peek_string (param_values + 2), + data2); +} + diff --git a/lib/marshal.h b/lib/marshal.h new file mode 100644 index 0000000..3487fbb --- /dev/null +++ b/lib/marshal.h @@ -0,0 +1,20 @@ + +#ifndef __g_cclosure_user_marshal_MARSHAL_H__ +#define __g_cclosure_user_marshal_MARSHAL_H__ + +#include + +G_BEGIN_DECLS + +/* VOID:POINTER,STRING (marshal.list:1) */ +extern void g_cclosure_user_marshal_VOID__POINTER_STRING (GClosure *closure, + GValue *return_value, + guint n_param_values, + const GValue *param_values, + gpointer invocation_hint, + gpointer marshal_data); + +G_END_DECLS + +#endif /* __g_cclosure_user_marshal_MARSHAL_H__ */ + diff --git a/lib/marshal.list b/lib/marshal.list new file mode 100644 index 0000000..0f3317f --- /dev/null +++ b/lib/marshal.list @@ -0,0 +1 @@ +VOID:POINTER,STRING diff --git a/lib/message.c b/lib/message.c new file mode 100644 index 0000000..ac73081 --- /dev/null +++ b/lib/message.c @@ -0,0 +1,269 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "include.h" + +#include "message.h" + +enum { + P_ID = 1, + P_FLAGS, + P_FROM, + P_TO, + P_CTIME, + P_RTIME, + P_APP, + P_BODY, +}; + +G_DEFINE_TYPE (CcnetMessage, ccnet_message, G_TYPE_OBJECT); + + +static void +set_property (GObject *object, guint property_id, + const GValue *v, GParamSpec *pspec) +{ + /* CcnetMessage *message = CCNET_MESSAGE (object); */ + + switch (property_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + return; + } +} + +static void +get_property (GObject *object, guint property_id, + GValue *v, GParamSpec *pspec) +{ + CcnetMessage *message = CCNET_MESSAGE (object); + + switch (property_id) { + case P_ID: + g_value_set_string (v, message->id); + break; + case P_FLAGS: + g_value_set_uint (v, message->flags); + break; + case P_FROM: + g_value_set_string (v, message->from); + break; + case P_TO: + g_value_set_string (v, message->to); + break; + case P_CTIME: + g_value_set_uint (v, message->ctime); + break; + case P_RTIME: + g_value_set_uint (v, message->rtime); + break; + case P_APP: + g_value_set_string (v, message->app); + break; + case P_BODY: + g_value_set_string (v, message->body); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void finalize (GObject *object) +{ + CcnetMessage *message = (CcnetMessage *)object; + g_free (message->app); + g_free (message->id); + g_free (message->body); +} + + +static void +ccnet_message_class_init (CcnetMessageClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = set_property; + gobject_class->get_property = get_property; + gobject_class->finalize = finalize; + + + g_object_class_install_property (gobject_class, P_ID, + g_param_spec_string ("id", NULL, "ID", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_FLAGS, + g_param_spec_uint ("flags", NULL, "Flags", + 0, UINT_MAX, 0, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_FROM, + g_param_spec_string ("from_id", NULL, "From ID", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_TO, + g_param_spec_string ("to_id", NULL, "To ID", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_CTIME, + g_param_spec_uint ("ctime", NULL, "Creation Time", + 0, UINT_MAX, 0, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_RTIME, + g_param_spec_uint ("rtime", NULL, "Receiving Time", + 0, UINT_MAX, 0, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_APP, + g_param_spec_string ("app", NULL, "application", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_BODY, + g_param_spec_string ("body", NULL, "message body", + NULL, G_PARAM_READWRITE)); + +} + +static void +ccnet_message_init (CcnetMessage *message) +{ +} + +CcnetMessage * +ccnet_message_new_full (const char *from, + const char *to, + const char *app, + const char *body, + time_t ctime, + time_t rcv_time, + const char *msg_id, + int flags) +{ + CcnetMessage *message; + + if (!from || !to || !app) + return NULL; + + message = g_object_new (CCNET_TYPE_MESSAGE, NULL); + + message->flags = flags; + memcpy (message->from, from, 40); + message->from[40] = '\0'; + memcpy (message->to, to, 40); + message->to[40] = '\0'; + message->app = g_strdup(app); + message->body = g_strdup(body); + message->ctime = (ctime ? ctime : time(NULL)); + message->rtime = rcv_time; + message->id = (msg_id ? g_strdup (msg_id) : ccnet_util_gen_uuid()); + + return message; +} + +CcnetMessage * +ccnet_message_new (const char *from, + const char *to, + const char *app, + const char *body, + int flags) +{ + return ccnet_message_new_full (from, to, app, body, 0, 0, NULL, flags); +} + +void +ccnet_message_free (CcnetMessage *message) +{ + g_object_unref (message); +} + + +void +ccnet_message_to_string_buf (CcnetMessage *msg, GString *buf) +{ + g_string_printf (buf, "%d %s %s %s %d %d %s %s", msg->flags, + msg->from, + msg->to, + msg->id, + (int)msg->ctime, + (int)msg->rtime, + msg->app, + msg->body); +} + +CcnetMessage * +ccnet_message_from_string (char *buf, int len) +{ + char flags; + int is_group_msg; + char *from_id, *to_id, *msg_id, *body, *p, *end, *app; + int ctime, rcv_time = 0; + CcnetMessage *message; + + g_return_val_if_fail (buf[len-1] == '\0', NULL); + + p = buf + 1; + while (*p != ' ' && *p) ++p; + if (*p != ' ') + goto error; + *p = '\0'; + flags = atoi (buf); + is_group_msg = flags & FLAG_TO_GROUP; + + from_id = ++p; + p += 40; + g_return_val_if_fail (*p == ' ', NULL); + *p = '\0'; + + to_id = ++p; + if (!is_group_msg) + p += 40; /* SHA-1 */ + else + p += 36; /* UUID */ + g_return_val_if_fail (*p == ' ', NULL); + *p = '\0'; + + msg_id = ++p; + p += 36; + g_return_val_if_fail (*p == ' ', NULL); + *p++ = '\0'; + + end = strchr (p, ' '); + *end = '\0'; + ctime = atoi (p); + + p = end + 1; + end = strchr (p, ' '); + *end = '\0'; + rcv_time = atoi (p); + + p = app = end + 1; + while (*p != ' ' && *p) ++p; + if (*p != ' ') + goto error; + *p = '\0'; + body = p + 1; + + message = ccnet_message_new_full (from_id, to_id, + app, body, + ctime, rcv_time, + msg_id, flags); + return message; + +error: + return NULL; +} + +gboolean +ccnet_message_is_to_group(CcnetMessage *msg) +{ + return msg->flags & FLAG_TO_GROUP; +} + +void +ccnet_message_body_take (CcnetMessage *msg, char *body) +{ + msg->body = body; +} + +void +ccnet_message_body_dup (CcnetMessage *msg, char *body) +{ + msg->body = g_strdup (body); +} diff --git a/lib/mqclient-proc.c b/lib/mqclient-proc.c new file mode 100644 index 0000000..ebf8209 --- /dev/null +++ b/lib/mqclient-proc.c @@ -0,0 +1,151 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include + +#include + +#include "ccnet-client.h" +#include "mqclient-proc.h" + +#define SC_MSG "300" +#define SC_UNSUBSCRIBE "301" + +enum { + INIT, + REQUEST_SENT, + READY +}; + +enum { + RECV_MSG_SIG, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static int mq_client_start (CcnetProcessor *processor, int argc, char **argv); + +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + + +G_DEFINE_TYPE (CcnetMqclientProc, ccnet_mqclient_proc, CCNET_TYPE_PROCESSOR) + +static void +ccnet_mqclient_proc_class_init (CcnetMqclientProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->start = mq_client_start; + proc_class->handle_response = handle_response; + proc_class->name = "mqclient-proc"; + + signals[RECV_MSG_SIG] = + g_signal_new ("recv-msg", CCNET_TYPE_MQCLIENT_PROC , + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, /* no accumulator */ + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, CCNET_TYPE_MESSAGE); +} + +static void +ccnet_mqclient_proc_init (CcnetMqclientProc *processor) +{ + CCNET_PROCESSOR (processor)->state = INIT; +} + +static int +mq_client_start (CcnetProcessor *processor, int argc, char **argv) +{ + GString *buf; + int i; + + buf = g_string_new ("mq-server"); + for (i = 0; i < argc; ++i) { + g_string_append (buf, " "); + g_string_append (buf, argv[i]); + } + + ccnet_client_send_request (processor->session, REQUEST_ID (processor->id), + buf->str); + processor->state = REQUEST_SENT; + + g_string_free (buf, TRUE); + return 0; +} + +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + CcnetMqclientProc *proc = (CcnetMqclientProc *) processor; + CcnetMessage *msg; + + switch (processor->state) { + case REQUEST_SENT: + if (code[0] != '2') { + g_warning ("receive bad response: %s %s\n", code, code_msg); + ccnet_processor_done (processor, FALSE); + return; + } + + processor->state = READY; + break; + case READY: + if (code[0] != '2' && code[0] != '3') { + g_warning ("receive bad response: %s %s\n", code, code_msg); + return; + } + + /* message notification. */ + if (code[0] == '3' && code[2] == '0') { + msg = ccnet_message_from_string (content, clen); + if (!msg) { + g_warning ("Wrong message format.\n"); + ccnet_processor_done (processor, FALSE); + break; + } + if (proc->message_got_cb) + proc->message_got_cb (msg, proc->cb_data); + g_signal_emit (proc, signals[RECV_MSG_SIG], 0, msg); + ccnet_message_free (msg); + } + + break; + default: + break; + } +} + +void ccnet_mqclient_proc_set_message_got_cb (CcnetMqclientProc *processor, + MessageGotCB callback, + void *cb_data) +{ + processor->message_got_cb = callback; + processor->cb_data = cb_data; +} + +void ccnet_mqclient_proc_put_message (CcnetMqclientProc *proc, + CcnetMessage *message) +{ + CcnetProcessor *processor = (CcnetProcessor *) proc; + GString *msg_buf; + + msg_buf = g_string_new (NULL); + + ccnet_message_to_string_buf (message, msg_buf); + ccnet_client_send_update (processor->session, UPDATE_ID(processor->id), + SC_MSG, NULL, msg_buf->str, msg_buf->len+1); + g_string_free (msg_buf, TRUE); +} + +void ccnet_mqclient_proc_unsubscribe_apps (CcnetMqclientProc *proc) +{ + CcnetProcessor *processor = (CcnetProcessor *) proc; + + ccnet_client_send_update (processor->session, UPDATE_ID(processor->id), + SC_UNSUBSCRIBE, NULL, NULL, 0); + ccnet_processor_done (processor, TRUE); +} diff --git a/lib/net.c b/lib/net.c new file mode 100644 index 0000000..5bd8a40 --- /dev/null +++ b/lib/net.c @@ -0,0 +1,468 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifdef WIN32 + #define WINVER 0x0501 +#endif +#include "include.h" + +#include +#include +#include +#include +#include + + +#ifdef WIN32 + #include + #include + #include + #include + #define UNUSED +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +#include + +#include "net.h" + + +#ifdef WIN32 + +#ifndef inet_aton +int inet_aton(const char *string, struct in_addr *addr) +{ + addr->s_addr = inet_addr(string); + if (addr->s_addr != -1 || strcmp("255.255.255.255", string) == 0) + return 1; + return 0; +} +#endif + +#endif //WIN32 + +int +ccnet_netSetTOS (evutil_socket_t s, int tos) +{ +#ifdef IP_TOS + return setsockopt( s, IPPROTO_IP, IP_TOS, (char*)&tos, sizeof( tos ) ); +#else + return 0; +#endif +} + +static evutil_socket_t +makeSocketNonBlocking (evutil_socket_t fd) +{ + if (fd >= 0) + { + if (evutil_make_socket_nonblocking(fd)) + { + ccnet_warning ("Couldn't make socket nonblock: %s", + evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR())); + evutil_closesocket(fd); + fd = -1; + } + } + return fd; +} + +static evutil_socket_t +createSocket (int family, int nonblock) +{ + evutil_socket_t fd; + int ret; + + fd = socket (family, SOCK_STREAM, 0); + + if (fd < 0) { + ccnet_warning("create Socket failed %d\n", fd); + } else if (nonblock) { + int nodelay = 1; + + fd = makeSocketNonBlocking( fd ); + + ret = setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, + (char *)&nodelay, sizeof(nodelay)); + if (ret < 0) { + ccnet_warning("setsockopt failed\n"); + evutil_closesocket(fd); + return -1; + } + } + + return fd; +} + +evutil_socket_t +ccnet_net_open_tcp (const struct sockaddr *sa, int nonblock) +{ + evutil_socket_t s; + int sa_len; + + if( (s = createSocket(sa->sa_family, nonblock)) < 0 ) + return -1; + +#ifndef WIN32 + if (sa->sa_family == AF_INET) + sa_len = sizeof (struct sockaddr_in); + else + sa_len = sizeof (struct sockaddr_in6); +#else + if (sa->sa_family == AF_INET) + sa_len = sizeof (struct sockaddr_in); + else + return -1; +#endif + + + if( (connect(s, sa, sa_len) < 0) +#ifdef WIN32 + && (sockerrno != WSAEWOULDBLOCK) +#endif + && (sockerrno != EINPROGRESS) ) + { + evutil_closesocket(s); + s = -1; + } + + return s; +} + +evutil_socket_t +ccnet_net_bind_tcp (int port, int nonblock) +{ +#ifndef WIN32 + int sockfd, n; + struct addrinfo hints, *res, *ressave; + char buf[10]; + + memset (&hints, 0,sizeof (struct addrinfo)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + snprintf (buf, sizeof(buf), "%d", port); + + if ( (n = getaddrinfo(NULL, buf, &hints, &res) ) != 0) { + ccnet_warning ("getaddrinfo fails: %s\n", gai_strerror(n)); + return -1; + } + + ressave = res; + + do { + int on = 1; + + sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sockfd < 0) + continue; /* error - try next one */ + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { + ccnet_warning ("setsockopt of SO_REUSEADDR error\n"); + continue; + } + + if (nonblock) + sockfd = makeSocketNonBlocking (sockfd); + if (sockfd < 0) + continue; /* error - try next one */ + + if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0) + break; /* success */ + + close(sockfd); /* bind error - close and try next one */ + } while ( (res = res->ai_next) != NULL); + + freeaddrinfo (ressave); + + if (res == NULL) { + ccnet_warning ("bind fails: %s\n", strerror(errno)); + return -1; + } + + return sockfd; +#else + + evutil_socket_t s; + struct sockaddr_in sock; + const int type = AF_INET; +#if defined( SO_REUSEADDR ) || defined( SO_REUSEPORT ) + int optval; +#endif + + if ((s = createSocket(type, nonblock)) < 0) + return -1; + + optval = 1; + setsockopt (s, SOL_SOCKET, SO_REUSEADDR, (char*)&optval, sizeof(optval)); + + memset(&sock, 0, sizeof(sock)); + sock.sin_family = AF_INET; + sock.sin_addr.s_addr = INADDR_ANY; + sock.sin_port = htons(port); + + if ( bind(s, (struct sockaddr *)&sock, sizeof(struct sockaddr_in)) < 0) + { + ccnet_warning ("bind fails: %s\n", strerror(errno)); + evutil_closesocket (s); + return -1; + } + if (nonblock) + s = makeSocketNonBlocking (s); + + return s; +#endif +} + +evutil_socket_t +ccnet_net_accept (evutil_socket_t b, struct sockaddr_storage *cliaddr, + socklen_t *len, int nonblock) +{ + evutil_socket_t s; + /* int nodelay = 1; */ + + s = accept (b, (struct sockaddr *)cliaddr, len); + + /* setsockopt (s, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay)); */ + if (nonblock) + makeSocketNonBlocking(s); + + return s; +} + + +evutil_socket_t +ccnet_net_bind_v4 (const char *ipaddr, int *port) +{ + evutil_socket_t sockfd; + struct sockaddr_in addr; + int on = 1; + + sockfd = socket (AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) { + ccnet_warning("create socket failed: %s\n", strerror(errno)); + exit(-1); + } + + memset (&addr, 0, sizeof (struct sockaddr_in)); + addr.sin_family = AF_INET; + if (inet_aton(ipaddr, &addr.sin_addr) == 0) { + ccnet_warning ("Bad ip address %s\n", ipaddr); + return -1; + } + addr.sin_port = htons (*port); + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) + { + ccnet_warning ("setsockopt of SO_REUSEADDR error: %s\n", + strerror(errno)); + return -1; + } + + if ( bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + ccnet_warning ("Bind error: %s\n", strerror (errno)); + return -1; + } + + + if (*port == 0) { + struct sockaddr_storage ss; + socklen_t len; + + len = sizeof(ss); + if (getsockname(sockfd, (struct sockaddr *)&ss, &len) < 0) { + ccnet_warning ("getsockname error: %s\n", strerror(errno)); + return -1; + } + *port = sock_port ((struct sockaddr *)&ss); + } + + return sockfd; +} + + + +char * +sock_ntop(const struct sockaddr *sa, socklen_t salen) +{ + static char str[128]; /* Unix domain is largest */ + + switch (sa->sa_family) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *) sa; + + if (evutil_inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL) + return(NULL); + return(str); + } + +#ifdef IPv6 + case AF_INET6: { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa; + + if (evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, str, sizeof(str) - 1) == NULL) + return(NULL); + return (str); + } +#endif + +#ifndef WIN32 +#ifdef AF_UNIX + case AF_UNIX: { + struct sockaddr_un *unp = (struct sockaddr_un *) sa; + + /* OK to have no pathname bound to the socket: happens on + every connect() unless client calls bind() first. */ + if (unp->sun_path[0] == 0) + strcpy(str, "(no pathname bound)"); + else + snprintf(str, sizeof(str), "%s", unp->sun_path); + return(str); + } +#endif +#endif + + default: + snprintf(str, sizeof(str), "sock_ntop: unknown AF_xxx: %d, len %d", + sa->sa_family, salen); + return(str); + } + return (NULL); +} + +int +sock_pton (const char *addr_str, uint16_t port, struct sockaddr_storage *sa) +{ + struct sockaddr_in *saddr = (struct sockaddr_in *) sa; + +#ifndef WIN32 + struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *) sa; +#endif + + if (evutil_inet_pton (AF_INET, addr_str, &saddr->sin_addr) == 1 ) { + saddr->sin_family = AF_INET; + saddr->sin_port = htons (port); + return 0; + } +#ifndef WIN32 + else if (evutil_inet_pton (AF_INET6, addr_str, &saddr6->sin6_addr) == 1) + { + saddr6->sin6_family = AF_INET6; + saddr6->sin6_port = htons (port); + return 0; + } +#endif + + return -1; +} + +/* return 1 if addr_str is a valid ipv4 or ipv6 address */ +int +is_valid_ipaddr (const char *addr_str) +{ + struct sockaddr_storage addr; + if (!addr_str) + return 0; + if (sock_pton(addr_str, 0, &addr) < 0) + return 0; + return 1; +} + +uint16_t +sock_port (const struct sockaddr *sa) +{ + switch (sa->sa_family) { + case AF_INET: { + struct sockaddr_in *sin = (struct sockaddr_in *) sa; + return ntohs(sin->sin_port); + } +#ifdef IPv6 + case AF_INET6: { + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa; + + return ntohs(sin6->sin6_port); + } +#endif + default: + return 0; + } + return 0; +} + + +evutil_socket_t +udp_client (const char *host, const char *serv, + struct sockaddr **saptr, socklen_t *lenp) +{ + evutil_socket_t sockfd; + int n; + struct addrinfo hints, *res, *ressave; + + memset (&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + if ((n = getaddrinfo(host, serv, &hints, &res)) != 0) { + ccnet_warning ("udp_client error for %s, %s: %s", + host, serv, gai_strerror(n)); + return -1; + } + ressave = res; + + do { + sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (sockfd >= 0) + break; /* success */ + } while ( (res = res->ai_next) != NULL); + + if (res == NULL) { /* errno set from final socket() */ + ccnet_warning ("udp_client error for %s, %s", host, serv); + freeaddrinfo (ressave); + return -1; + } + + *saptr = malloc(res->ai_addrlen); + memcpy(*saptr, res->ai_addr, res->ai_addrlen); + *lenp = res->ai_addrlen; + + freeaddrinfo(ressave); + + return (sockfd); +} + + +int +family_to_level(int family) +{ + switch (family) { + case AF_INET: + return IPPROTO_IP; +#ifdef IPV6 + case AF_INET6: + return IPPROTO_IPV6; +#endif + default: + return -1; + } +} + +int +sockfd_to_family(evutil_socket_t sockfd) +{ + struct sockaddr_storage ss; + socklen_t len; + + len = sizeof(ss); + if (getsockname(sockfd, (struct sockaddr *) &ss, &len) < 0) + return(-1); + return(ss.ss_family); +} diff --git a/lib/net.h b/lib/net.h new file mode 100644 index 0000000..730515b --- /dev/null +++ b/lib/net.h @@ -0,0 +1,72 @@ + +#ifndef CCNET_NET_H +#define CCNET_NET_H + +#ifdef WIN32 + #include + #include + #include + typedef int socklen_t; + #define UNUSED +#else + #include + #include + #include + #include + #include + #include + #include + #include +#endif + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#else +#include +#endif + +#ifdef WIN32 + /* #define ECONNREFUSED WSAECONNREFUSED */ + /* #define ECONNRESET WSAECONNRESET */ + /* #define EHOSTUNREACH WSAEHOSTUNREACH */ + /* #define EINPROGRESS WSAEINPROGRESS */ + /* #define ENOTCONN WSAENOTCONN */ + /* #define EWOULDBLOCK WSAEWOULDBLOCK */ + #define sockerrno WSAGetLastError( ) +#else + #include + #define sockerrno errno +#endif + +#ifdef WIN32 +extern int inet_aton(const char *string, struct in_addr *addr); +extern const char *inet_ntop(int af, const void *src, char *dst, size_t size); +extern int inet_pton(int af, const char *src, void *dst); +#endif + +evutil_socket_t ccnet_net_open_tcp (const struct sockaddr *sa, int nonblock); +evutil_socket_t ccnet_net_bind_tcp (int port, int nonblock); +evutil_socket_t ccnet_net_accept (evutil_socket_t b, + struct sockaddr_storage *cliaddr, + socklen_t *len, int nonblock); + +/* bind to an IPv4 address, if (*port == 0) the port number will be returned */ +evutil_socket_t ccnet_net_bind_v4 (const char *ipaddr, int *port); + +int ccnet_netSetTOS ( evutil_socket_t s, int tos ); + +char *sock_ntop(const struct sockaddr *sa, socklen_t salen); +uint16_t sock_port (const struct sockaddr *sa); + +/* return 1 if addr_str is a valid ipv4 or ipv6 address */ +int is_valid_ipaddr (const char *addr_str); + + +/* return 0 if success, -1 if error */ +int sock_pton (const char *addr_str, uint16_t port, + struct sockaddr_storage *sa); + +evutil_socket_t udp_client (const char *host, const char *serv, + struct sockaddr **saptr, socklen_t *lenp); + +#endif diff --git a/lib/packet-io.c b/lib/packet-io.c new file mode 100644 index 0000000..f5256ae --- /dev/null +++ b/lib/packet-io.c @@ -0,0 +1,239 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include + +#include + +#ifdef WIN32 + #include +#else + #include +#endif + +#include +#include +#include +#include + + +#include +#include + + +#include + +#include "packet.h" +#include "packet-io.h" +#include "buffer.h" + + +static ssize_t /* Write "n" bytes to a descriptor. */ +writen(evutil_socket_t fd, const void *vptr, size_t n) +{ + size_t nleft; + ssize_t nwritten; + const char *ptr; + + ptr = vptr; + nleft = n; + while (nleft > 0) { +#ifndef WIN32 + if ( (nwritten = write(fd, ptr, nleft)) <= 0) { +#else + if ( (nwritten = send(fd, (char *)ptr, nleft, 0)) <= 0) { +#endif + if (nwritten < 0 && errno == EINTR) + nwritten = 0; /* and call write() again */ + else + return(-1); /* error */ + } + + nleft -= nwritten; + ptr += nwritten; + } + return(n); +} + +static ssize_t /* Read "n" bytes from a descriptor. */ +readn(evutil_socket_t fd, struct buffer *buf, size_t n) +{ + size_t nleft; + ssize_t nread; + + nleft = n; + while (nleft > 0) { + if ( (nread = buffer_read(buf, fd, nleft)) < 0) { + if (errno == EINTR) + nread = 0; /* and call read() again */ + else + return(-1); + } else if (nread == 0) + break; /* EOF */ + + nleft -= nread; + } + return(n - nleft); /* return >= 0 */ +} + + +CcnetPacketIO* +ccnet_packet_io_new (evutil_socket_t fd) +{ + CcnetPacketIO *io; + + io = g_malloc (sizeof(CcnetPacketIO)); + io->fd = fd; + io->buffer = buffer_new (); + io->in_buf = buffer_new (); + + return io; +} + +void +ccnet_packet_io_free (CcnetPacketIO *io) +{ + evutil_closesocket(io->fd); + buffer_free (io->buffer); + buffer_free (io->in_buf); + g_free (io); +} + +void +ccnet_packet_prepare (CcnetPacketIO *io, int type, int id) +{ + ccnet_header header; + + header.version = 1; + header.type = type; + header.length = 0; + header.id = htonl (id); + buffer_add (io->buffer, &header, sizeof (header)); +} + + +void +ccnet_packet_write_string (CcnetPacketIO *io, const char *str) +{ + int len; + + len = strlen(str); + buffer_add (io->buffer, str, len); +} + +void +ccnet_packet_add (CcnetPacketIO *io, const char *buf, int len) +{ + buffer_add (io->buffer, buf, len); +} + +void +ccnet_packet_finish (CcnetPacketIO *io) +{ + ccnet_header *header; + header = (ccnet_header *) BUFFER_DATA(io->buffer); + header->length = htons (BUFFER_LENGTH(io->buffer) + - CCNET_PACKET_LENGTH_HEADER); +} + + +void +ccnet_packet_send (CcnetPacketIO *io) +{ + writen (io->fd, BUFFER_DATA (io->buffer), io->buffer->off); + buffer_drain (io->buffer, io->buffer->off); +} + + +void +ccnet_packet_finish_send (CcnetPacketIO *io) +{ + ccnet_packet_finish (io); + ccnet_packet_send (io); +} + +ccnet_packet * +ccnet_packet_io_read_packet (CcnetPacketIO* io) +{ + ccnet_packet *packet; + int len; + + buffer_drain (io->in_buf, io->in_buf->off); + + if (readn (io->fd, io->in_buf, CCNET_PACKET_LENGTH_HEADER) <= 0) + return NULL; + + packet = (ccnet_packet *) BUFFER_DATA(io->in_buf); + len = ntohs (packet->header.length); + if (len > 0) { + if (readn (io->fd, io->in_buf, len) <= 0) + return NULL; + } + + /* Note: must reset packet since readn() may cause realloc of buffer */ + packet = (ccnet_packet *) BUFFER_DATA(io->in_buf); + packet->header.length = len; + packet->header.id = ntohl (packet->header.id); + + return packet; +} + +void +ccnet_packet_io_set_callback (CcnetPacketIO *io, + got_packet_callback func, + void *user_data) +{ + io->func = func; + io->user_data = user_data; +} + +/* return 0 on EOF, -1 on error, 1 otherwise */ +int +ccnet_packet_io_read (CcnetPacketIO *io) +{ + int n; + ccnet_packet *packet; + int len; + +again: + if ( (n = buffer_read(io->in_buf, io->fd, 1024)) < 0) { + if (errno == EINTR) + goto again; + + g_warning ("read from connfd error: %s.\n", strerror(errno)); + return -1; + } + + if (n == 0) { + if (io->func) + io->func (NULL, io->user_data); + return 0; + } + + while (BUFFER_LENGTH(io->in_buf) >= CCNET_PACKET_LENGTH_HEADER) + { + packet = (ccnet_packet *) BUFFER_DATA(io->in_buf); + len = ntohs (packet->header.length); + + if (BUFFER_LENGTH (io->in_buf) - CCNET_PACKET_LENGTH_HEADER < len) + break; + + packet->header.length = len; + packet->header.id = ntohl (packet->header.id); + + io->func (packet, io->user_data); + buffer_drain (io->in_buf, len + CCNET_PACKET_LENGTH_HEADER); + } + + return 1; +} + + +/* void */ +/* ccnet_send_request (int req_id, const char *req) */ +/* { */ +/* ccnet_packet_prepear (CCNET_MSG_REQUEST, req_id); */ +/* ccnet_packet_write_string (req); */ +/* ccnet_packet_finish_send (); */ + +/* fprintf (stderr, "Send a request: id %d, cmd %s\n", req_id, req); */ +/* } */ diff --git a/lib/packet-io.h b/lib/packet-io.h new file mode 100644 index 0000000..1be5cfb --- /dev/null +++ b/lib/packet-io.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_CLI_IO_H +#define CCNET_CLI_IO_H + +#include + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#else +#include +#endif + +struct buffer; + +typedef struct CcnetPacketIO CcnetPacketIO; + +typedef void (*got_packet_callback) (ccnet_packet *packet, void *user_data); + +struct CcnetPacketIO { + evutil_socket_t fd; + + struct buffer *buffer; + + struct buffer *in_buf; + + got_packet_callback func; + void *user_data; +}; + +CcnetPacketIO* ccnet_packet_io_new (evutil_socket_t fd); + +void ccnet_packet_io_free (CcnetPacketIO *io); + +void ccnet_packet_prepare (CcnetPacketIO *io, int type, int id); +void ccnet_packet_write_string (CcnetPacketIO *io, const char *str); +void ccnet_packet_add (CcnetPacketIO *io, const char *buf, int len); +void ccnet_packet_finish (CcnetPacketIO *io); +void ccnet_packet_send (CcnetPacketIO *io); +void ccnet_packet_finish_send (CcnetPacketIO *io); + +void ccnet_packet_io_set_callback (CcnetPacketIO *io, + got_packet_callback func, + void *user_data); + +int ccnet_packet_io_read (CcnetPacketIO *io); + +ccnet_packet* ccnet_packet_io_read_packet (CcnetPacketIO* io); + +#endif diff --git a/lib/peer-common.h b/lib/peer-common.h new file mode 100644 index 0000000..d6bbeff --- /dev/null +++ b/lib/peer-common.h @@ -0,0 +1,304 @@ +#ifdef CCNET_LIB + #define string_list_parse_sorted ccnet_util_string_list_parse_sorted + #define string_list_free ccnet_util_string_list_free + #define string_list_join ccnet_util_string_list_join +#endif + +G_DEFINE_TYPE (CcnetPeer, ccnet_peer, G_TYPE_OBJECT); + +enum { + P_ID = 1, + P_IS_SELF, + P_NAME, + P_PUBLIC_ADDR, + P_PUBLIC_PORT, + P_SERVICE_URL, + P_IP, + P_PORT, + P_AUTH_STATE, + P_NET_STATE, + P_PUBKEY, + P_CAN_CONNECT, /* can be connected */ + P_IN_LOCAL_NET, + P_IN_CONNECTION, + P_IS_READY, + P_ROLE_LIST, + P_MY_ROLE_LIST, + P_SESSION_KEY, + P_ENCRYPT_CHANNEL, +}; + +static void +get_property (GObject *object, guint property_id, + GValue *v, GParamSpec *pspec) +{ + CcnetPeer *peer = (CcnetPeer *)object; + GString *buf; + + switch (property_id) { + case P_ID: + g_value_set_string (v, peer->id); + break; + case P_IS_SELF: + g_value_set_boolean (v, peer->is_self); + break; + case P_NAME: + g_value_set_string (v, peer->name); + break; + case P_PUBLIC_ADDR: + g_value_set_string (v, peer->public_addr); + break; + case P_PUBLIC_PORT: + g_value_set_int (v, peer->public_port); + break; + case P_IP: + g_value_set_string (v, peer->addr_str); + break; + case P_PORT: + g_value_set_int (v, peer->port); + break; + case P_SERVICE_URL: + g_value_set_string (v, peer->service_url); + break; + case P_NET_STATE: + g_value_set_int (v, peer->net_state); + break; + case P_PUBKEY: +#ifndef CCNET_LIB + if (peer->pubkey) { + GString *str = public_key_to_gstring(peer->pubkey); + g_value_set_string (v, str->str); + g_string_free(str, TRUE); + } else + g_value_set_string (v, NULL); +#else + g_value_set_string (v, NULL); +#endif + break; + case P_CAN_CONNECT: + g_value_set_boolean (v, peer->can_connect); + break; + case P_IN_LOCAL_NET: + g_value_set_boolean (v, peer->in_local_network); + break; + case P_IN_CONNECTION: + g_value_set_boolean (v, peer->in_connection); + break; + case P_IS_READY: + g_value_set_boolean (v, peer->is_ready); + break; + case P_ROLE_LIST: + buf = g_string_new (NULL); + string_list_join (peer->role_list, buf, ","); + g_value_take_string (v, buf->str); + g_string_free (buf, FALSE); + break; + case P_MY_ROLE_LIST: + buf = g_string_new (NULL); + string_list_join (peer->myrole_list, buf, ","); + g_value_take_string (v, buf->str); + g_string_free (buf, FALSE); + break; + case P_SESSION_KEY: + g_value_set_string (v, peer->session_key); + break; + case P_ENCRYPT_CHANNEL: + g_value_set_boolean (v, peer->encrypt_channel); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + +static void +set_roles (CcnetPeer *peer, const char *roles) +{ + if (!roles) + return; + GList *role_list = string_list_parse_sorted (roles, ","); + + string_list_free (peer->role_list); + peer->role_list = role_list; +} + +static void +set_my_roles (CcnetPeer *peer, const char *roles) +{ + if (!roles) + return; + GList *role_list = string_list_parse_sorted (roles, ","); + + string_list_free (peer->myrole_list); + peer->myrole_list = role_list; +} + +static void +set_property_common (GObject *object, guint property_id, + const GValue *v, GParamSpec *pspec) +{ + CcnetPeer *peer = (CcnetPeer *)object; + + switch (property_id) { + case P_ID: + memcpy(peer->id, g_value_get_string(v), 41); + break; + case P_NAME: + g_free (peer->name); + peer->name = g_value_dup_string(v); + break; + case P_IS_SELF: + peer->is_self = g_value_get_boolean(v); + break; + case P_PUBLIC_ADDR: + g_free (peer->public_addr); + peer->public_addr = g_value_dup_string(v); + break; + case P_PUBLIC_PORT: + peer->public_port = g_value_get_int (v); + break; + case P_SERVICE_URL: + g_free (peer->service_url); + peer->service_url = g_value_dup_string(v); + break; + case P_IP: + g_free (peer->addr_str); + peer->addr_str = g_value_dup_string(v); + break; + case P_PORT: + peer->port = g_value_get_int (v); + break; + case P_NET_STATE: + peer->net_state = g_value_get_int (v); + break; + case P_PUBKEY: +#ifndef CCNET_LIB + if (peer->pubkey) + RSA_free(peer->pubkey); + peer->pubkey = public_key_from_string ((char *)g_value_get_string(v)); +#endif + break; + case P_CAN_CONNECT: + peer->can_connect = g_value_get_boolean (v); + break; + case P_IN_LOCAL_NET: + peer->in_local_network = g_value_get_boolean (v); + break; + case P_IN_CONNECTION: + peer->in_connection = g_value_get_boolean (v); + break; + case P_IS_READY: + peer->is_ready = g_value_get_boolean (v); + break; + case P_ROLE_LIST: + set_roles (peer, g_value_get_string(v)); + break; + case P_MY_ROLE_LIST: + set_my_roles (peer, g_value_get_string(v)); + break; + case P_SESSION_KEY: + g_free (peer->session_key); + peer->session_key = g_value_dup_string (v); + break; + case P_ENCRYPT_CHANNEL: + peer->encrypt_channel = g_value_get_boolean (v); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + return; + } +} + +static void +define_properties (GObjectClass *gobject_class) +{ + g_object_class_install_property (gobject_class, P_ID, + g_param_spec_string ("id", NULL, "Node ID", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_NAME, + g_param_spec_string ("name", NULL, "Hostname", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_PUBLIC_ADDR, + g_param_spec_string ("public-addr", NULL, "Public Addrress", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_PUBLIC_PORT, + g_param_spec_int ("public-port", NULL, "Public Port", + 0, 65535, 0, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_SERVICE_URL, + g_param_spec_string ("service-url", NULL, "Service Url", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_IP, + g_param_spec_string ("ip", NULL, "Dynamic IP", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_PORT, + g_param_spec_int ("port", NULL, "Dynamic Port", + 0, 65535, 0, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_NET_STATE, + g_param_spec_int ("net-state", NULL, "Network State", + -1, 3, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_IS_SELF, + g_param_spec_boolean ("is-self", NULL, "Is self", + 0, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_PUBKEY, + g_param_spec_string ("pubkey", NULL, "Public Key", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_CAN_CONNECT, + g_param_spec_boolean ("can-connect", NULL, "Can be connect via TCP", + 0, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_IN_LOCAL_NET, + g_param_spec_boolean ("in-local-network", NULL, "In local network", + 0, G_PARAM_READWRITE)); + + + g_object_class_install_property (gobject_class, P_IN_CONNECTION, + g_param_spec_boolean ("in-connection", NULL, "in connection", + 0, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_IS_READY, + g_param_spec_boolean ("is_ready", NULL, "service ready", + 0, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_ROLE_LIST, + g_param_spec_string ("role-list", NULL, "Role list", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_MY_ROLE_LIST, + g_param_spec_string ("myrole-list", NULL, "My role list", + NULL, G_PARAM_READWRITE)); + + + g_object_class_install_property (gobject_class, P_SESSION_KEY, + g_param_spec_string ("session-key", NULL, "session key", + NULL, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, P_ENCRYPT_CHANNEL, + g_param_spec_boolean ("encrypt-channel", NULL, "encrypt channel", + 0, G_PARAM_READWRITE)); +} + + +const char* +ccnet_peer_get_net_state_string (int net_state) +{ + switch (net_state) { + case PEER_DOWN: + return "down"; + case PEER_CONNECTED: + return "connected"; + default: + return "unknown"; + } +} diff --git a/lib/peer.c b/lib/peer.c new file mode 100644 index 0000000..73ebc4c --- /dev/null +++ b/lib/peer.c @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "include.h" +#include "peer.h" +#include "ccnet-client.h" +#include "peer-common.h" + +static void ccnet_peer_free (GObject *object); + + +static void +set_property (GObject *object, guint property_id, + const GValue *v, GParamSpec *pspec) +{ + set_property_common (object, property_id, v, pspec); +} + +static void +ccnet_peer_class_init (CcnetPeerClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->set_property = set_property; + gobject_class->get_property = get_property; + gobject_class->finalize = ccnet_peer_free; + + define_properties (gobject_class); +} + +static void +ccnet_peer_init (CcnetPeer *peer) +{ +} + + +CcnetPeer* +ccnet_peer_new (const char *id) +{ + CcnetPeer *peer; + + peer = g_object_new (CCNET_TYPE_PEER, NULL); + memcpy (peer->id, id, 40); + peer->id[40] = '\0'; + + peer->net_state = PEER_DOWN; + peer->public_port = 0; + peer->port = 0; + return peer; +} + +void +ccnet_peer_free (GObject *object) +{ + CcnetPeer *peer = CCNET_PEER (object); + GList *ptr; + + g_free (peer->name); + g_free (peer->addr_str); + g_free (peer->service_url); + g_free (peer->public_addr); + g_free (peer->login_error); + g_free (peer->bind_email); + g_free (peer->session_key); + for (ptr = peer->role_list; ptr; ptr = ptr->next) + g_free (ptr->data); + g_list_free (peer->role_list); + + for (ptr = peer->myrole_list; ptr; ptr = ptr->next) + g_free (ptr->data); + g_list_free (peer->myrole_list); + + G_OBJECT_CLASS(ccnet_peer_parent_class)->finalize (object); +} + diff --git a/lib/proc-factory.c b/lib/proc-factory.c new file mode 100644 index 0000000..b334882 --- /dev/null +++ b/lib/proc-factory.c @@ -0,0 +1,215 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "include.h" +#include "processor.h" +#include "ccnet-client.h" + +#include "peer.h" +#include "proc-factory.h" + +typedef struct { + GHashTable *proc_type_table; +} CcnetProcFactoryPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_PROC_FACTORY, CcnetProcFactoryPriv)) + +G_DEFINE_TYPE (CcnetProcFactory, ccnet_proc_factory, G_TYPE_OBJECT) + +static void +ccnet_proc_factory_free (GObject *factory); + +static void +ccnet_proc_factory_class_init (CcnetProcFactoryClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = ccnet_proc_factory_free; + + g_type_class_add_private (klass, sizeof (CcnetProcFactoryPriv)); +} + +static void +ccnet_proc_factory_init (CcnetProcFactory *factory) +{ + CcnetProcFactoryPriv *priv = GET_PRIV (factory); + + priv->proc_type_table = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, NULL); +} + +static void +ccnet_proc_factory_free (GObject *factory) +{ + CcnetProcFactoryPriv *priv = GET_PRIV (factory); + + g_hash_table_destroy (priv->proc_type_table); +} + +void +ccnet_proc_factory_register_processor (CcnetProcFactory *factory, + const char *serv_name, + GType proc_type) +{ + CcnetProcFactoryPriv *priv = GET_PRIV (factory); + + CcnetProcessorClass *proc_class = + (CcnetProcessorClass *)g_type_class_ref(proc_type); + g_type_class_unref (proc_class); + + /* check dumplication */ + if (g_hash_table_lookup(priv->proc_type_table, serv_name)) + return; + + g_hash_table_insert (priv->proc_type_table, g_strdup (serv_name), + (gpointer) proc_type); +} + +GType ccnet_sendcmd_proc_get_type (); +GType ccnet_mqclient_proc_get_type (); +GType ccnet_async_rpc_proc_get_type (); + +CcnetProcFactory *ccnet_proc_factory_new (CcnetClient *session) +{ + CcnetProcFactory *factory; + + factory = g_object_new (CCNET_TYPE_PROC_FACTORY, NULL); + factory->session = session; + + /* register fundamental processors */ + + ccnet_proc_factory_register_processor (factory, "send-cmd", + ccnet_sendcmd_proc_get_type ()); + /* ccnet_proc_factory_register_processor (factory, "send-event", */ + /* ccnet_sendevent_proc_get_type ()); */ + ccnet_proc_factory_register_processor (factory, "mq-client", + ccnet_mqclient_proc_get_type ()); + + ccnet_proc_factory_register_processor (factory, "async-rpc", + ccnet_async_rpc_proc_get_type ()); + + return factory; +} + +static GType ccnet_proc_factory_get_proc_type (CcnetProcFactory *factory, + const char *serv_name) +{ + CcnetProcFactoryPriv *priv = GET_PRIV (factory); + + return (GType) g_hash_table_lookup (priv->proc_type_table, serv_name); +} + +CcnetProcessor * +ccnet_proc_factory_create_processor (CcnetProcFactory *factory, + const char *serv_name, + int is_master, + int req_id) +{ + GType type; + CcnetProcessor *processor; + + type = ccnet_proc_factory_get_proc_type (factory, serv_name); + if (type == 0) { + g_warning ("No processor for service: %s.\n", serv_name); + return NULL; + } + + processor = g_object_new (type, NULL); + processor->session = factory->session; + if (is_master) { + if (req_id == 0) + processor->id = MASTER_ID ( + ccnet_client_get_request_id (factory->session)); + else + processor->id = MASTER_ID (req_id); + } else + processor->id = SLAVE_ID (req_id); + + /* Set the service this processor provide. + * This may be different from the processor class name. + */ + processor->name = g_strdup(serv_name); + + ccnet_client_add_processor (factory->session, processor); + + return processor; +} + + +CcnetProcessor * +ccnet_proc_factory_create_master_processor (CcnetProcFactory *factory, + const char *serv_name) +{ + return ccnet_proc_factory_create_processor (factory, serv_name, + MASTER, 0); +} + +CcnetProcessor * +ccnet_proc_factory_create_remote_master_processor (CcnetProcFactory *factory, + const char *serv_name, + const char *peer_id) +{ + GType type; + CcnetProcessor *processor; + + type = ccnet_proc_factory_get_proc_type (factory, serv_name); + if (type == 0) { + ccnet_warning ("No such processor type: %s.\n", serv_name); + return NULL; + } + + + processor = g_object_new (type, NULL); + processor->peer_id = g_strdup(peer_id); + processor->session = factory->session; + processor->id = MASTER_ID (ccnet_client_get_request_id (factory->session)); + + /* Set the real processor name. + * This may be different from the processor class name. + */ + processor->name = g_strdup(serv_name); + + ccnet_client_add_processor (factory->session, processor); + + return processor; +} + + +CcnetProcessor * +ccnet_proc_factory_create_slave_processor (CcnetProcFactory *factory, + const char *serv_name, + const char *peer_id, + int req_id) +{ + GType type; + CcnetProcessor *processor; + + type = ccnet_proc_factory_get_proc_type (factory, serv_name); + if (type == 0) { + g_warning ("No such processor type: %s.\n", serv_name); + return NULL; + } + + processor = g_object_new (type, NULL); + processor->peer_id = g_strdup(peer_id); + processor->session = factory->session; + processor->id = SLAVE_ID (req_id); + + /* Set the real processor name. + * This may be different from the processor class name. + */ + processor->name = g_strdup(serv_name); + + ccnet_client_add_processor (factory->session, processor); + + return processor; +} + + +void +ccnet_proc_factory_recycle (CcnetProcFactory *factory, + CcnetProcessor *processor) +{ + ccnet_client_remove_processor (factory->session, processor); + g_object_unref (processor); +} diff --git a/lib/processor.c b/lib/processor.c new file mode 100644 index 0000000..6c3d2b9 --- /dev/null +++ b/lib/processor.c @@ -0,0 +1,353 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "include.h" + +#include + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#else +#include +#endif + +#include "ccnet-client.h" +#include "processor.h" +#include "proc-factory.h" +#include "timer.h" +#include "peer.h" +#include "job-mgr.h" + +G_DEFINE_TYPE (CcnetProcessor, ccnet_processor, G_TYPE_OBJECT); + +static void default_shutdown (CcnetProcessor *processor); +static void default_release_resource (CcnetProcessor *processor); + +enum { + DONE_SIG, /* connection down */ + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void +ccnet_processor_class_init (CcnetProcessorClass *klass) +{ + /* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */ + + klass->start = NULL; + klass->handle_update = NULL; + klass->handle_response = NULL; + klass->shutdown = default_shutdown; + klass->release_resource = default_release_resource; + + + signals[DONE_SIG] = + g_signal_new ("done", CCNET_TYPE_PROCESSOR, + G_SIGNAL_RUN_LAST, + 0, /* no class singal handler */ + NULL, NULL, /* no accumulator */ + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); +} + +static void +ccnet_processor_init (CcnetProcessor *processor) +{ + +} + +int ccnet_processor_start (CcnetProcessor *processor, int argc, char **argv) +{ + if (!processor->session->connected) { + g_warning ("[proc] Not connected to daemon.\n"); + return -1; + } + + processor->failure = PROC_NOTSET; + + return CCNET_PROCESSOR_GET_CLASS (processor)->start (processor, argc, argv); +} + +int ccnet_processor_startl (CcnetProcessor *processor, ...) +{ + va_list ap; + int argc = 0; + char **argv = g_malloc (sizeof(char *) * 10); + char *arg; + int max = 10; + int ret; + + va_start (ap, processor); + arg = va_arg (ap, char *); + while (arg) { + if (argc >= max) { + max *= 2; + argv = realloc (argv, sizeof(char *) * max); + } + argv[argc++] = arg; + + arg = va_arg (ap, char *); + } + va_end (ap); + + ret = ccnet_processor_start (processor, argc, argv); + g_free (argv); + + return ret; +} + +static void default_shutdown (CcnetProcessor *processor) +{ + +} + +static void default_release_resource(CcnetProcessor *processor) +{ + g_free (processor->name); + g_free (processor->peer_id); + if (processor->timer) + ccnet_timer_free (&processor->timer); +} + +/* should be called before recycle */ +void +ccnet_processor_release_resource(CcnetProcessor *processor) +{ + CCNET_PROCESSOR_GET_CLASS (processor)->release_resource(processor); +} + +void +ccnet_processor_done (CcnetProcessor *processor, + gboolean success) +{ + if (processor->thread_running) { + processor->delay_shutdown = TRUE; + processor->was_success = success; + return; + } + + if (processor->state == STATE_IN_SHUTDOWN) { + return; + } + processor->state = STATE_IN_SHUTDOWN; + if (processor->failure == PROC_NOTSET && success) + processor->failure = PROC_DONE; + + g_debug ("[proc] Processor %s(%d) done %d\n", GET_PNAME(processor), + PRINT_ID(processor->id), success); + + /* Notify */ + if (!IS_SLAVE (processor) && success) { + ccnet_processor_send_update (processor, SC_PROC_DONE, SS_PROC_DONE, + NULL, 0); + } + + g_signal_emit (processor, signals[DONE_SIG], 0, success); + + ccnet_client_remove_processor (processor->session, processor); + ccnet_processor_release_resource (processor); + ccnet_proc_factory_recycle (processor->session->proc_factory, processor); +} + +void ccnet_processor_handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + g_object_ref (processor); + processor->is_active = TRUE; + + if (code[0] == '5') { + ccnet_debug ("[Proc] Shutdown processor %s(%d) for bad update: %s %s\n", + GET_PNAME(processor), PRINT_ID(processor->id), + code, code_msg); + + if (memcmp(code, SC_UNKNOWN_SERVICE, 3) == 0) + processor->failure = PROC_NO_SERVICE; + else if (memcmp(code, SC_PERM_ERR, 3) == 0) + processor->failure = PROC_PERM_ERR; + else if (memcmp(code, SC_NETDOWN, 3) == 0) + processor->failure = PROC_REMOTE_DEAD; + else + processor->failure = PROC_BAD_RESP; + + ccnet_processor_done (processor, FALSE); + return; + } + + if (strncmp (code, SC_PROC_KEEPALIVE, 3) == 0) { + ccnet_processor_send_response (processor, SC_PROC_ALIVE, + SS_PROC_ALIVE, NULL, 0); + } else if (strncmp (code, SC_PROC_DEAD, 3) == 0) { + ccnet_debug ("[proc] Shutdown processor %s(%d) when peer(%.8s) processor is dead\n", + GET_PNAME(processor), PRINT_ID(processor->id), + processor->peer_id); + processor->failure = PROC_REMOTE_DEAD; + ccnet_processor_done (processor, FALSE); + } else if (strncmp (code, SC_PROC_DONE, 3) == 0) { + ccnet_debug ("[proc] Shutdown processor when receive 103: service done\n"); + ccnet_processor_done (processor, TRUE); + } else { + CCNET_PROCESSOR_GET_CLASS (processor)->handle_update (processor, + code, code_msg, + content, clen); + } + processor->is_active = FALSE; + g_object_unref (processor); +} + +void ccnet_processor_handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + g_return_if_fail (CCNET_PROCESSOR_GET_CLASS(processor)->handle_response != NULL); + + g_object_ref (processor); + processor->is_active = TRUE; + + if (code[0] == '5') { + ccnet_debug ("[Proc] Shutdown processor %s(%d) for bad response: %s %s from %s\n", + GET_PNAME(processor), PRINT_ID(processor->id), + code, code_msg, processor->peer_id); + if (memcmp(code, SC_UNKNOWN_SERVICE, 3) == 0) + processor->failure = PROC_NO_SERVICE; + else if (memcmp(code, SC_PERM_ERR, 3) == 0) + processor->failure = PROC_PERM_ERR; + else if (memcmp(code, SC_NETDOWN, 3) == 0) + processor->failure = PROC_REMOTE_DEAD; + else + processor->failure = PROC_BAD_RESP; + + ccnet_processor_done (processor, FALSE); + return; + } + + if (strncmp (code, SC_PROC_KEEPALIVE, 3) == 0) { + ccnet_processor_send_update (processor, SC_PROC_ALIVE, + SS_PROC_ALIVE, NULL, 0); + } else if (strncmp (code, SC_PROC_DEAD, 3) == 0) { + ccnet_debug ("[proc] Shutdown processor %s(%d) when peer(%.8s) processor is dead\n", + GET_PNAME(processor), PRINT_ID(processor->id), + processor->peer_id); + processor->failure = PROC_REMOTE_DEAD; + ccnet_processor_done (processor, FALSE); + } else { + CCNET_PROCESSOR_GET_CLASS (processor)->handle_response (processor, + code, code_msg, + content, clen); + } + processor->is_active = FALSE; + g_object_unref (processor); +} + + +void ccnet_processor_handle_sigchld (CcnetProcessor *processor, int status) +{ + CCNET_PROCESSOR_GET_CLASS (processor)->handle_sigchld (processor, + status); +} + +void +ccnet_processor_send_request (CcnetProcessor *processor, + const char *request) +{ + ccnet_client_send_request (processor->session, + REQUEST_ID (processor->id), + request); +} + +void +ccnet_processor_send_request_l (CcnetProcessor *processor, ...) +{ + va_list ap; + GString *buf = g_string_new(NULL); + char *arg; + + va_start (ap, processor); + arg = va_arg (ap, char *); + while (arg) { + g_string_append (buf, arg); + arg = va_arg (ap, char *); + } + va_end (ap); + + ccnet_client_send_request (processor->session, + REQUEST_ID (processor->id), + buf->str); + + g_string_free (buf, TRUE); +} + + +void +ccnet_processor_send_update(CcnetProcessor *processor, + const char *code, + const char *code_msg, + const char *content, int clen) +{ + ccnet_client_send_update (processor->session, UPDATE_ID(processor->id), + code, code_msg, content, clen); +} + +void +ccnet_processor_send_response(CcnetProcessor *processor, + const char *code, + const char *code_msg, + const char *content, int clen) +{ + ccnet_client_send_response (processor->session, RESPONSE_ID(processor->id), + code, code_msg, content, clen); +} + +typedef struct ProcThreadData { + CcnetProcessor *proc; + ProcThreadFunc func; + void *data; + ProcThreadDoneFunc done_func; + void *result; +} ProcThreadData; + +static void +processor_thread_done (void *vdata) +{ + ProcThreadData *tdata = vdata; + + tdata->proc->thread_running = FALSE; + + if (tdata->proc->delay_shutdown) + ccnet_processor_done (tdata->proc, tdata->proc->was_success); + else + tdata->done_func (tdata->result); + + g_free (tdata); +} + +static void * +processor_thread_func_wrapper (void *vdata) +{ + ProcThreadData *tdata = vdata; + tdata->result = tdata->func (tdata->data); + return vdata; +} + +int +ccnet_processor_thread_create (CcnetProcessor *processor, + CcnetJobManager *job_mgr, + ProcThreadFunc func, + ProcThreadDoneFunc done_func, + void *data) +{ + ProcThreadData *tdata; + + tdata = g_new(ProcThreadData, 1); + tdata->proc = processor; + tdata->func = func; + tdata->done_func = done_func; + tdata->data = data; + + ccnet_job_manager_schedule_job (job_mgr ? job_mgr : processor->session->job_mgr, + processor_thread_func_wrapper, + processor_thread_done, + tdata); + processor->thread_running = TRUE; + return 0; +} diff --git a/lib/rpc-common.h b/lib/rpc-common.h new file mode 100644 index 0000000..b798f38 --- /dev/null +++ b/lib/rpc-common.h @@ -0,0 +1,41 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef RPC_COMMON_H +#define RPC_COMMON_H + +#include "packet.h" + +#define SC_CLIENT_CALL "301" +#define SS_CLIENT_CALL "CLIENT CALL" +#define SC_CLIENT_MORE "302" +#define SS_CLIENT_MORE "MORE" +#define SC_SERVER_RET "311" +#define SS_SERVER_RET "SERVER RET" +#define SC_SERVER_MORE "312" +#define SS_SERVER_MORE "HAS MORE" +#define SC_SERVER_ERR "411" +#define SS_SERVER_ERR "Fail to invoke the function, check the function" + +/* MESSAGE_HEADER = SC_SERVER_RET(3) + " " + SS_SERVER_RET(10) + "\n"(1) + "\n"(1) */ +#define MESSAGE_HEADER 64 /* leave enough space */ +#define MAX_TRANSFER_LENGTH (CCNET_PACKET_MAX_PAYLOAD_LEN - MESSAGE_HEADER) + +/* + Client Server + -rpcserver + ----------------------> + + 200 OK + <---------------------- + 301 Func String + ----------------------> + + 312 HAS MORE + <----------------------- + 302 MORE + ----------------------> + 311 SERVER RET + <----------------------- + */ + +#endif diff --git a/lib/rpc_table.py b/lib/rpc_table.py new file mode 100644 index 0000000..5fde127 --- /dev/null +++ b/lib/rpc_table.py @@ -0,0 +1,63 @@ +""" +Define RPC functions needed to generate +""" + +# [ , [] ] +func_table = [ + [ "int", [] ], + [ "int", ["int"] ], + [ "int", ["int", "int"] ], + [ "int", ["int", "string"] ], + [ "int", ["int", "string", "int"] ], + [ "int", ["int", "string", "string"] ], + [ "int", ["int", "string", "int", "int"] ], + [ "int", ["string"] ], + [ "int", ["string", "int"] ], + [ "int", ["string", "int", "string"] ], + [ "int", ["string", "int", "string", "string"] ], + [ "int", ["string", "int", "string", "int", "int"] ], + [ "int", ["string", "string"] ], + [ "int", ["string", "string", "string"] ], + [ "int", ["string", "string", "int"] ], + [ "int", ["string", "string", "int", "int"] ], + [ "int", ["string", "string", "string", "string"] ], + [ "int", ["string", "string", "string", "string", "string"] ], + [ "int", ["string", "string", "string", "string", "string", "string"] ], + [ "int", ["string", "string", "string", "string", "string", "string", "string"] ], + [ "int", ["string", "int64"]], + [ "int", ["int", "int64"]], + [ "int", ["int", "string", "int64"]], + [ "int64", [] ], + [ "int64", ["string"] ], + [ "int64", ["int"]], + [ "int64", ["int", "string"]], + [ "string", [] ], + [ "string", ["int"] ], + [ "string", ["string"] ], + [ "string", ["string", "int"] ], + [ "string", ["string", "string"] ], + [ "string", ["string", "string", "string"] ], + [ "string", ["string", "string", "string", "string"] ], + [ "string", ["string", "string", "string", "string", "int"] ], + [ "string", ["string", "string", "string", "string", "string"] ], + [ "string", ["string", "string", "string", "string", "string", "string"] ], + [ "string", ["string", "string", "string", "string", "string", "int"] ], + [ "string", ["string", "string", "string", "string", "string", "string", "string", "string", "string"] ], + [ "objlist", [] ], + [ "objlist", ["int"] ], + [ "objlist", ["int", "int"] ], + [ "objlist", ["int", "int", "int"] ], + [ "objlist", ["string"] ], + [ "objlist", ["string", "int"] ], + [ "objlist", ["string", "int", "int"] ], + [ "objlist", ["string", "int", "int", "string"] ], + [ "objlist", ["string", "string"] ], + [ "objlist", ["string", "string", "string"] ], + [ "objlist", ["string", "string", "int"] ], + [ "objlist", ["string", "string", "int", "int"] ], + [ "objlist", ["int", "int", "string"] ], + [ "object", [] ], + [ "object", ["int"] ], + [ "object", ["string"] ], + [ "object", ["string", "string"] ], +] diff --git a/lib/rpcserver-proc.c b/lib/rpcserver-proc.c new file mode 100644 index 0000000..7a1c0ec --- /dev/null +++ b/lib/rpcserver-proc.c @@ -0,0 +1,116 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "include.h" + +#include +#include +#include "searpc-server.h" +#include "rpc-common.h" + +typedef struct { + char *buf; + int len; + int off; +} CcnetRpcserverProcPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_RPCSERVER_PROC, CcnetRpcserverProcPriv)) + +G_DEFINE_TYPE (CcnetRpcserverProc, ccnet_rpcserver_proc, CCNET_TYPE_PROCESSOR) + +static int start (CcnetProcessor *processor, int argc, char **argv); +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void +release_resource(CcnetProcessor *processor) +{ + CCNET_PROCESSOR_CLASS (ccnet_rpcserver_proc_parent_class)->release_resource (processor); +} + + +static void +ccnet_rpcserver_proc_class_init (CcnetRpcserverProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->start = start; + proc_class->handle_update = handle_update; + proc_class->release_resource = release_resource; + proc_class->name = "rpcserver-proc"; + + g_type_class_add_private (klass, sizeof(CcnetRpcserverProcPriv)); +} + +static void +ccnet_rpcserver_proc_init (CcnetRpcserverProc *processor) +{ +} + + +static int +start (CcnetProcessor *processor, int argc, char **argv) +{ + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + + return 0; +} + + +static void +handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + CcnetRpcserverProcPriv *priv = GET_PRIV (processor); + + if (memcmp (code, SC_CLIENT_CALL, 3) == 0) { + gsize ret_len; + char *svc_name = processor->name; + char *ret = searpc_server_call_function (svc_name, content, clen, &ret_len); + + if (ret_len < MAX_TRANSFER_LENGTH) { + ccnet_processor_send_response ( + processor, SC_SERVER_RET, SS_SERVER_RET, ret, ret_len); + g_free (ret); + /* ccnet_processor_done (processor, TRUE); */ + return; + } + + /* we need to split data into multiple segments */ + priv->buf = ret; + priv->len = ret_len; + priv->off = 0; + + ccnet_processor_send_response (processor, SC_SERVER_MORE, + SS_SERVER_MORE, priv->buf, + MAX_TRANSFER_LENGTH); + priv->off = MAX_TRANSFER_LENGTH; + + return; + } + + if (memcmp (code, SC_CLIENT_MORE, 3) == 0) { + if (priv->off + MAX_TRANSFER_LENGTH < priv->len) { + ccnet_processor_send_response ( + processor, SC_SERVER_MORE, SS_SERVER_MORE, + priv->buf + priv->off, MAX_TRANSFER_LENGTH); + priv->off += MAX_TRANSFER_LENGTH; + } else { + ccnet_processor_send_response ( + processor, SC_SERVER_RET, SS_SERVER_RET, + priv->buf + priv->off, priv->len - priv->off); + g_free (priv->buf); + /* ccnet_processor_done (processor, TRUE); */ + } + return; + } + + ccnet_processor_send_response (processor, SC_BAD_UPDATE_CODE, + SS_BAD_UPDATE_CODE, NULL, 0); + g_warning ("[rpc-server] Bad update: %s %s.\n", code, code_msg); + if (priv->buf) + g_free (priv->buf); + ccnet_processor_done (processor, FALSE); +} diff --git a/lib/rsa.c b/lib/rsa.c new file mode 100644 index 0000000..63fdd1a --- /dev/null +++ b/lib/rsa.c @@ -0,0 +1,161 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include +#include +#include +#include + +#include +#include + +#include "rsa.h" +#include "utils.h" + +RSA* +private_key_to_pub(RSA *priv) +{ + RSA *pub = RSA_new(); + + pub->n = BN_dup(priv->n); + pub->e = BN_dup(priv->e); + + return pub; +} + + +GString* public_key_to_gstring(const RSA *rsa) +{ + GString *buf = g_string_new(NULL); + unsigned char *temp; + char *coded; + + gsize len = BN_num_bytes(rsa->n); + temp = malloc(len); + BN_bn2bin(rsa->n, temp); + coded = g_base64_encode(temp, len); + g_string_append (buf, coded); + g_string_append_c (buf, ' '); + g_free(coded); + + len = BN_num_bytes(rsa->e); + temp = realloc(temp, len); + BN_bn2bin(rsa->e, temp); + coded = g_base64_encode(temp, len); + g_string_append (buf, coded); + g_free(coded); + + free(temp); + + return buf; +} + +void +public_key_append_to_gstring(const RSA *rsa, GString *buf) +{ + unsigned char *temp; + char *coded; + + gsize len = BN_num_bytes(rsa->n); + temp = malloc(len); + BN_bn2bin(rsa->n, temp); + coded = g_base64_encode(temp, len); + g_string_append (buf, coded); + g_string_append_c (buf, ' '); + g_free(coded); + + len = BN_num_bytes(rsa->e); + temp = realloc(temp, len); + BN_bn2bin(rsa->e, temp); + coded = g_base64_encode(temp, len); + g_string_append (buf, coded); + g_free(coded); + + free(temp); +} + +RSA* public_key_from_string(char *str) +{ + char *p; + unsigned char *num; + gsize len; + if (!str) + return NULL; + + if ( !(p = strchr(str, ' ')) ) + return NULL; + *p = '\0'; + + RSA *key = RSA_new(); + + num = g_base64_decode(str, &len); + key->n = BN_bin2bn(num, len, NULL); + if (!key->n) + goto err; + g_free(num); + + num = g_base64_decode(p+1, &len); + key->e = BN_bin2bn(num, len, NULL); + if (!key->e) + goto err; + g_free(num); + + *p = ' '; + return key; +err: + *p = ' '; + RSA_free (key); + g_free(num); + return NULL; +} + +unsigned char * +private_key_decrypt(RSA *key, unsigned char *data, int len, int *decrypt_len) +{ + int size; + unsigned char *buf; + + size = RSA_size(key); + buf = g_malloc(size); + *decrypt_len = RSA_private_decrypt(len, data, buf, key, RSA_PKCS1_PADDING); + + return buf; +} + +unsigned char * +public_key_encrypt(RSA *key, unsigned char *data, int len, int *encrypt_len) +{ + int size; + unsigned char *buf; + + size = RSA_size(key); + buf = g_malloc(size); + *encrypt_len = RSA_public_encrypt(len, data, buf, key, RSA_PKCS1_PADDING); + + return buf; +} + +char * +id_from_pubkey (RSA *pubkey) +{ + GString *buf; + unsigned char sha1[20]; + char *id = g_malloc(41); + + buf = public_key_to_gstring (pubkey); + calculate_sha1 (sha1, buf->str); + sha1_to_hex (sha1, id); + g_string_free (buf, TRUE); + + return id; +} + +RSA * +generate_private_key(u_int bits) +{ + RSA *private = NULL; + + private = RSA_generate_key(bits, 35, NULL, NULL); + if (private == NULL) + g_error ("rsa_generate_private_key: key generation failed."); + return private; +} diff --git a/lib/rsa.h b/lib/rsa.h new file mode 100644 index 0000000..47657cc --- /dev/null +++ b/lib/rsa.h @@ -0,0 +1,31 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_RSA_H +#define CCNET_RSA_H + +#include +#include +#include +#include + + +RSA* private_key_to_pub(RSA *priv); + +GString* public_key_to_gstring(const RSA *rsa); +void public_key_append_to_gstring(const RSA *rsa, GString *buf); + +RSA* public_key_from_string(char *str); + +unsigned char* private_key_decrypt(RSA *key, unsigned char *data, + int len, int *decrypt_len); + +unsigned char* public_key_encrypt(RSA *key, unsigned char *data, + int len, int *encrypt_len); + + +char *id_from_pubkey (RSA *pubkey); + +RSA* generate_private_key(u_int bits); + + +#endif diff --git a/lib/sendcmd-proc.c b/lib/sendcmd-proc.c new file mode 100644 index 0000000..b8fa57c --- /dev/null +++ b/lib/sendcmd-proc.c @@ -0,0 +1,136 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include + +#include +#include + +#include "ccnet-client.h" +#include "sendcmd-proc.h" + +enum { + INIT, + REQUEST_SENT, + CONNECTED +}; + +typedef struct { + int persist : 1; +} CcnetSendcmdProcPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_SENDCMD_PROC, CcnetSendcmdProcPriv)) + +static int send_cmd_start (CcnetProcessor *processor, int argc, char **argv); + +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + + +G_DEFINE_TYPE (CcnetSendcmdProc, ccnet_sendcmd_proc, CCNET_TYPE_PROCESSOR); + +static void +ccnet_sendcmd_proc_class_init (CcnetSendcmdProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->start = send_cmd_start; + proc_class->name = "sendcmd-proc"; + proc_class->handle_response = handle_response; + + g_type_class_add_private (klass, sizeof (CcnetSendcmdProcPriv)); +} + +static void +ccnet_sendcmd_proc_init (CcnetSendcmdProc *processor) +{ + CCNET_PROCESSOR (processor)->state = INIT; +} + + +static int +send_cmd_start (CcnetProcessor *processor, int argc, char **argv) +{ + char buf[256]; + CcnetSendcmdProcPriv *priv = GET_PRIV (processor); + + priv->persist = 0; + + while (0 < argc && argv[0][0] == '-') { + switch (argv[0][1]) { + case 'p': + priv->persist = 1; + break; + default: + break; + } + argc--; argv++; + } + if (priv->persist) { + snprintf (buf, 256, "receive-cmd -p"); + } else { + snprintf (buf, 256, "receive-cmd"); + } + ccnet_client_send_request (processor->session, + REQUEST_ID (processor->id), buf); + processor->state = REQUEST_SENT; + + return 0; +} + + + +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + CcnetSendcmdProcPriv *priv = GET_PRIV (processor); + CcnetSendcmdProc *proc = (CcnetSendcmdProc *) processor; + + if (code[0] != '2') { + g_warning ("Receive bad response %s %s\n", code, code_msg); + } + + switch (processor->state) { + case REQUEST_SENT: + processor->state = CONNECTED; + return; + + case CONNECTED: + if (proc->rcvrsp_cb) { + proc->rcvrsp_cb (code, content, clen, proc->cb_data); + } + break; + default: + break; + } + + if (!priv->persist) + ccnet_processor_done (processor, TRUE); +} + +int +ccnet_sendcmd_proc_send_command (CcnetSendcmdProc *proc, const char *cmd) +{ + CcnetProcessor *processor = CCNET_PROCESSOR (proc); + +/* + if (processor->state != CONNECTED) + return -1; +*/ + ccnet_client_send_update (processor->session, UPDATE_ID(processor->id), + "200", NULL, + cmd, strlen(cmd)+1); /* including '\0' */ + + return 0; +} + +void +ccnet_sendcmd_proc_set_rcvrsp_cb (CcnetSendcmdProc *proc, + SendcmdProcRcvrspCallback rcvrsp_cb, + void *data) +{ + proc->rcvrsp_cb = rcvrsp_cb; + proc->cb_data = data; +} diff --git a/lib/string-util.h b/lib/string-util.h new file mode 100644 index 0000000..c115c3a --- /dev/null +++ b/lib/string-util.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_STRING_H +#define CCNET_STRING_H + +#include + +#define END_0(buf,len) (buf[(len)-1] == '\0') + + +static inline int get_version(char *str) +{ + int v; + + if (str[0] != 'v') + return 0; + if (sscanf(str+1, "%d", &v) != 1) + return 0; + return v; +} + + + +#define sgoto_next(p) do { \ + while (*p != ' ' && *p) ++p; \ + if (*p != ' ') \ + goto error; \ + *p = '\0'; \ + ++p; \ + } while (0) + +#define sget_len(val, p) do { \ + char *tmp = p; \ + sgoto_next(p); \ + val = atoi(tmp); \ + if (val == 0) \ + goto error; \ + } while (0) + + +/* get a string with format "%s " */ +#define sget_str(str, p) do { \ + str = p; \ + sgoto_next(p); \ + } while (0) + +/* get a string with format "%d %s " */ +#define sget_str_with_len(str, p) do { \ + int len; \ + sget_len(len, p); \ + str = p; \ + p += len; \ + *p = '\0'; \ + ++p; \ + } while (0) + + + +#endif diff --git a/lib/threaded-rpcserver-proc.c b/lib/threaded-rpcserver-proc.c new file mode 100644 index 0000000..ddebf83 --- /dev/null +++ b/lib/threaded-rpcserver-proc.c @@ -0,0 +1,155 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "include.h" + +#include +#include +#include +#include "searpc-server.h" +#include "rpc-common.h" +#include "job-mgr.h" + +typedef struct { + char *call_buf; + gsize call_len; + char *buf; + gsize len; + int off; + char *error_message; +} CcnetThreadedRpcserverProcPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_THREADED_RPCSERVER_PROC, CcnetThreadedRpcserverProcPriv)) + +G_DEFINE_TYPE (CcnetThreadedRpcserverProc, ccnet_threaded_rpcserver_proc, CCNET_TYPE_PROCESSOR) + +static int start (CcnetProcessor *processor, int argc, char **argv); +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void +release_resource(CcnetProcessor *processor) +{ + CcnetThreadedRpcserverProcPriv *priv = GET_PRIV(processor); + + g_free (priv->buf); + + CCNET_PROCESSOR_CLASS (ccnet_threaded_rpcserver_proc_parent_class)->release_resource (processor); +} + + +static void +ccnet_threaded_rpcserver_proc_class_init (CcnetThreadedRpcserverProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->start = start; + proc_class->handle_update = handle_update; + proc_class->release_resource = release_resource; + proc_class->name = "threaded-rpcserver-proc"; + + g_type_class_add_private (klass, sizeof(CcnetThreadedRpcserverProcPriv)); +} + +static void +ccnet_threaded_rpcserver_proc_init (CcnetThreadedRpcserverProc *processor) +{ +} + + +static int +start (CcnetProcessor *processor, int argc, char **argv) +{ + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + + return 0; +} + +static void * +call_function_job (void *vprocessor) +{ + CcnetProcessor *processor = vprocessor; + CcnetThreadedRpcserverProcPriv *priv = GET_PRIV(processor); + char *svc_name = processor->name; + + priv->buf = searpc_server_call_function (svc_name, priv->call_buf, priv->call_len, + &priv->len); + g_free (priv->call_buf); + + return vprocessor; +} + +static void +call_function_done (void *vprocessor) +{ + CcnetProcessor *processor = vprocessor; + CcnetThreadedRpcserverProcPriv *priv; + + priv = GET_PRIV(processor); + + if (priv->buf) { + if (priv->len < MAX_TRANSFER_LENGTH) { + ccnet_processor_send_response (processor, SC_SERVER_RET, SS_SERVER_RET, + priv->buf, priv->len); + g_free (priv->buf); + priv->buf = NULL; + /* ccnet_processor_done (processor, TRUE); */ + return; + } + + /* we need to split data into multiple segments */ + ccnet_processor_send_response (processor, SC_SERVER_MORE, + SS_SERVER_MORE, priv->buf, + MAX_TRANSFER_LENGTH); + priv->off = MAX_TRANSFER_LENGTH; + } else { + char *message = priv->error_message ? priv->error_message : ""; + ccnet_processor_send_response (processor, SC_SERVER_ERR, + message, + NULL, 0); + g_free (priv->error_message); + ccnet_processor_done (processor, FALSE); + } +} + +static void +handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + CcnetThreadedRpcserverProcPriv *priv = GET_PRIV (processor); + + if (memcmp (code, SC_CLIENT_CALL, 3) == 0) { + priv->call_buf = g_memdup (content, clen); + priv->call_len = (gsize)clen; + ccnet_processor_thread_create (processor, + NULL, + call_function_job, + call_function_done, + processor); + return; + } + + if (memcmp (code, SC_CLIENT_MORE, 3) == 0) { + if (priv->off + MAX_TRANSFER_LENGTH < priv->len) { + ccnet_processor_send_response ( + processor, SC_SERVER_MORE, SS_SERVER_MORE, + priv->buf + priv->off, MAX_TRANSFER_LENGTH); + priv->off += MAX_TRANSFER_LENGTH; + } else { + ccnet_processor_send_response ( + processor, SC_SERVER_RET, SS_SERVER_RET, + priv->buf + priv->off, priv->len - priv->off); + g_free (priv->buf); + priv->buf = NULL; + /* ccnet_processor_done (processor, TRUE); */ + } + return; + } + + ccnet_processor_send_response (processor, SC_BAD_UPDATE_CODE, + SS_BAD_UPDATE_CODE, NULL, 0); + g_warning ("[rpc-server] Bad update: %s %s.\n", code, code_msg); + ccnet_processor_done (processor, FALSE); +} diff --git a/lib/timer.c b/lib/timer.c new file mode 100644 index 0000000..cbce46c --- /dev/null +++ b/lib/timer.c @@ -0,0 +1,81 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#include +#include +#else +#include +#endif + +#include + +#ifdef CCNET_LIB + #include "libccnet_utils.h" + #define timeval_from_msec ccnet_util_timeval_from_msec +#else + #include "utils.h" +#endif + +#include "timer.h" + +struct CcnetTimer +{ + struct event event; + struct timeval tv; + TimerCB func; + void *user_data; + uint8_t inCallback; +}; + +static void +timer_callback (evutil_socket_t fd, short event, void *vtimer) +{ + int more; + struct CcnetTimer *timer = vtimer; + + timer->inCallback = 1; + more = (*timer->func) (timer->user_data); + timer->inCallback = 0; + + if (more) + evtimer_add (&timer->event, &timer->tv); + else + ccnet_timer_free (&timer); +} + +void +ccnet_timer_free (CcnetTimer **ptimer) +{ + CcnetTimer *timer; + + /* zero out the argument passed in */ + g_return_if_fail (ptimer); + + timer = *ptimer; + *ptimer = NULL; + + /* destroy the timer directly or via the command queue */ + if (timer && !timer->inCallback) + { + event_del (&timer->event); + g_free (timer); + } +} + +CcnetTimer* +ccnet_timer_new (TimerCB func, + void *user_data, + uint64_t interval_milliseconds) +{ + CcnetTimer *timer = g_new0 (CcnetTimer, 1); + + timer->tv = timeval_from_msec (interval_milliseconds); + timer->func = func; + timer->user_data = user_data; + + evtimer_set (&timer->event, timer_callback, timer); + evtimer_add (&timer->event, &timer->tv); + + return timer; +} diff --git a/lib/utils.c b/lib/utils.c new file mode 100644 index 0000000..ce61db3 --- /dev/null +++ b/lib/utils.c @@ -0,0 +1,1657 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include + +#include "utils.h" + +#ifdef WIN32 + #include + #include + #include + #include + #include +#else + #include +#endif + +#ifndef WIN32 +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +#include + +extern int inet_pton(int af, const char *src, void *dst); + + +struct timeval +timeval_from_msec (uint64_t milliseconds) +{ + struct timeval ret; + const uint64_t microseconds = milliseconds * 1000; + ret.tv_sec = microseconds / 1000000; + ret.tv_usec = microseconds % 1000000; + return ret; +} + +void +rawdata_to_hex (const unsigned char *rawdata, char *hex_str, int n_bytes) +{ + static const char hex[] = "0123456789abcdef"; + int i; + + for (i = 0; i < n_bytes; i++) { + unsigned int val = *rawdata++; + *hex_str++ = hex[val >> 4]; + *hex_str++ = hex[val & 0xf]; + } + *hex_str = '\0'; +} + +static unsigned hexval(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + return ~0; +} + +int +hex_to_rawdata (const char *hex_str, unsigned char *rawdata, int n_bytes) +{ + int i; + for (i = 0; i < n_bytes; i++) { + unsigned int val = (hexval(hex_str[0]) << 4) | hexval(hex_str[1]); + if (val & ~0xff) + return -1; + *rawdata++ = val; + hex_str += 2; + } + return 0; +} + +size_t +ccnet_strlcpy (char *dest, const char *src, size_t size) +{ + size_t ret = strlen(src); + + if (size) { + size_t len = (ret >= size) ? size - 1 : ret; + memcpy(dest, src, len); + dest[len] = '\0'; + } + return ret; +} + + +int +checkdir (const char *dir) +{ + struct stat st; + +#ifdef WIN32 + /* remove trailing '\\' */ + char *path = g_strdup(dir); + char *p = (char *)path + strlen(path) - 1; + while (*p == '\\' || *p == '/') *p-- = '\0'; + if ((g_stat(dir, &st) < 0) || !S_ISDIR(st.st_mode)) { + g_free (path); + return -1; + } + g_free (path); + return 0; +#else + if ((g_stat(dir, &st) < 0) || !S_ISDIR(st.st_mode)) + return -1; + return 0; +#endif +} + +int +checkdir_with_mkdir (const char *dir) +{ +#ifdef WIN32 + int ret; + char *path = g_strdup(dir); + char *p = (char *)path + strlen(path) - 1; + while (*p == '\\' || *p == '/') *p-- = '\0'; + ret = g_mkdir_with_parents(path, 0755); + g_free (path); + return ret; +#else + return g_mkdir_with_parents(dir, 0755); +#endif +} + +int +objstore_mkdir (const char *base) +{ + int ret; + int i, j, len; + static const char hex[] = "0123456789abcdef"; + char subdir[PATH_MAX]; + + if ( (ret = checkdir_with_mkdir(base)) < 0) + return ret; + + len = strlen(base); + memcpy(subdir, base, len); + subdir[len] = G_DIR_SEPARATOR; + subdir[len+3] = '\0'; + + for (i = 0; i < 16; i++) { + subdir[len+1] = hex[i]; + for (j = 0; j < 16; j++) { + subdir[len+2] = hex[j]; + if ( (ret = checkdir_with_mkdir(subdir)) < 0) + return ret; + } + } + return 0; +} + +void +objstore_get_path (char *path, const char *base, const char *obj_id) +{ + int len; + + len = strlen(base); + memcpy(path, base, len); + path[len] = G_DIR_SEPARATOR; + path[len+1] = obj_id[0]; + path[len+2] = obj_id[1]; + path[len+3] = G_DIR_SEPARATOR; + strcpy(path+len+4, obj_id+2); +} + + +ssize_t /* Read "n" bytes from a descriptor. */ +readn(int fd, void *vptr, size_t n) +{ + size_t nleft; + ssize_t nread; + char *ptr; + + ptr = vptr; + nleft = n; + while (nleft > 0) { + if ( (nread = read(fd, ptr, nleft)) < 0) { + if (errno == EINTR) + nread = 0; /* and call read() again */ + else + return(-1); + } else if (nread == 0) + break; /* EOF */ + + nleft -= nread; + ptr += nread; + } + return(n - nleft); /* return >= 0 */ +} + +ssize_t /* Write "n" bytes to a descriptor. */ +writen(int fd, const void *vptr, size_t n) +{ + size_t nleft; + ssize_t nwritten; + const char *ptr; + + ptr = vptr; + nleft = n; + while (nleft > 0) { + if ( (nwritten = write(fd, ptr, nleft)) <= 0) { + if (nwritten < 0 && errno == EINTR) + nwritten = 0; /* and call write() again */ + else + return(-1); /* error */ + } + + nleft -= nwritten; + ptr += nwritten; + } + return(n); +} + + +ssize_t /* Read "n" bytes from a descriptor. */ +recvn(evutil_socket_t fd, void *vptr, size_t n) +{ + size_t nleft; + ssize_t nread; + char *ptr; + + ptr = vptr; + nleft = n; + while (nleft > 0) { +#ifndef WIN32 + if ( (nread = read(fd, ptr, nleft)) < 0) +#else + if ( (nread = recv(fd, ptr, nleft, 0)) < 0) +#endif + { + if (errno == EINTR) + nread = 0; /* and call read() again */ + else + return(-1); + } else if (nread == 0) + break; /* EOF */ + + nleft -= nread; + ptr += nread; + } + return(n - nleft); /* return >= 0 */ +} + +ssize_t /* Write "n" bytes to a descriptor. */ +sendn(evutil_socket_t fd, const void *vptr, size_t n) +{ + size_t nleft; + ssize_t nwritten; + const char *ptr; + + ptr = vptr; + nleft = n; + while (nleft > 0) { +#ifndef WIN32 + if ( (nwritten = write(fd, ptr, nleft)) <= 0) +#else + if ( (nwritten = send(fd, ptr, nleft, 0)) <= 0) +#endif + { + if (nwritten < 0 && errno == EINTR) + nwritten = 0; /* and call write() again */ + else + return(-1); /* error */ + } + + nleft -= nwritten; + ptr += nwritten; + } + return(n); +} + +int copy_fd (int ifd, int ofd) +{ + while (1) { + char buffer[8192]; + ssize_t len = readn (ifd, buffer, sizeof(buffer)); + if (!len) + break; + if (len < 0) { + close (ifd); + return -1; + } + if (writen (ofd, buffer, len) < 0) { + close (ofd); + return -1; + } + } + close(ifd); + return 0; +} + +int copy_file (const char *dst, const char *src, int mode) +{ + int fdi, fdo, status; + + if ((fdi = g_open (src, O_RDONLY | O_BINARY, 0)) < 0) + return fdi; + + fdo = g_open (dst, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, mode); + if (fdo < 0 && errno == EEXIST) { + close (fdi); + return 0; + } else if (fdo < 0){ + close (fdi); + return -1; + } + + status = copy_fd (fdi, fdo); + if (close (fdo) != 0) + return -1; + + return status; +} + +char* +ccnet_expand_path (const char *src) +{ +#ifdef WIN32 + char new_path[PATH_MAX + 1]; + char *p = new_path; + const char *q = src; + + memset(new_path, 0, sizeof(new_path)); + if (*src == '~') { + const char *home = g_get_home_dir(); + memcpy(new_path, home, strlen(home)); + p += strlen(new_path); + q++; + } + memcpy(p, q, strlen(q)); + + /* delete the charactor '\' or '/' at the end of the path + * because the function stat faied to deal with directory names + * with '\' or '/' in the end */ + p = new_path + strlen(new_path) - 1; + while(*p == '\\' || *p == '/') *p-- = '\0'; + + return strdup (new_path); +#else + const char *next_in, *ntoken; + char new_path[PATH_MAX + 1]; + char *next_out; + int len; + + /* special cases */ + if (!src || *src == '\0') + return NULL; + if (strlen(src) > PATH_MAX) + return NULL; + + next_in = src; + next_out = new_path; + *next_out = '\0'; + + if (*src == '~') { + /* handle src start with '~' or '~' like '~plt' */ + struct passwd *pw = NULL; + + for ( ; *next_in != '/' && *next_in != '\0'; next_in++) ; + + len = next_in - src; + if (len == 1) { + pw = getpwuid (geteuid()); + } else { + /* copy '~' to new_path */ + memcpy (new_path, src, len); + new_path[len] = '\0'; + pw = getpwnam (new_path + 1); + } + if (pw == NULL) + return NULL; + + len = strlen (pw->pw_dir); + memcpy (new_path, pw->pw_dir, len); + next_out = new_path + len; + *next_out = '\0'; + + if (*next_in == '\0') + return strdup (new_path); + } else if (*src != '/') { + getcwd (new_path, PATH_MAX); + for ( ; *next_out; next_out++) ; /* to '\0' */ + } + + while (*next_in != '\0') { + /* move ntoken to the next not '/' char */ + for (ntoken = next_in; *ntoken == '/'; ntoken++) ; + + for (next_in = ntoken; *next_in != '/' + && *next_in != '\0'; next_in++) ; + + len = next_in - ntoken; + + if (len == 0) { + /* the path ends with '/', keep it */ + *next_out++ = '/'; + *next_out = '\0'; + break; + } + + if (len == 2 && ntoken[0] == '.' && ntoken[1] == '.') + { + /* '..' */ + for (; next_out > new_path && *next_out != '/'; next_out--) + ; + *next_out = '\0'; + } else if (ntoken[0] != '.' || len != 1) { + /* not '.' */ + *next_out++ = '/'; + memcpy (next_out, ntoken, len); + next_out += len; + *next_out = '\0'; + } + } + + /* the final special case */ + if (new_path[0] == '\0') { + new_path[0] = '/'; + new_path[1] = '\0'; + } + return strdup (new_path); +#endif +} + + +int +calculate_sha1 (unsigned char *sha1, const char *msg) +{ + SHA_CTX c; + + SHA1_Init(&c); + SHA1_Update(&c, msg, strlen(msg)); + SHA1_Final(sha1, &c); + return 0; +} + +uint32_t +ccnet_sha1_hash (const void *v) +{ + /* 31 bit hash function */ + const unsigned char *p = v; + uint32_t h = 0; + int i; + + for (i = 0; i < 20; i++) + h = (h << 5) - h + p[i]; + + return h; +} + +int +ccnet_sha1_equal (const void *v1, + const void *v2) +{ + const unsigned char *p1 = v1; + const unsigned char *p2 = v2; + int i; + + for (i = 0; i < 20; i++) + if (p1[i] != p2[i]) + return 0; + + return 1; +} + +#ifndef WIN32 +char* gen_uuid () +{ + char *uuid_str = g_malloc (37); + uuid_t uuid; + + uuid_generate (uuid); + uuid_unparse_lower (uuid, uuid_str); + + return uuid_str; +} + +void gen_uuid_inplace (char *buf) +{ + uuid_t uuid; + + uuid_generate (uuid); + uuid_unparse_lower (uuid, buf); +} + +gboolean +is_uuid_valid (const char *uuid_str) +{ + uuid_t uuid; + + if (uuid_parse (uuid_str, uuid) < 0) + return FALSE; + return TRUE; +} + +#else +char* gen_uuid () +{ + char *uuid_str = g_malloc (37); + unsigned char *str = NULL; + UUID uuid; + + UuidCreate(&uuid); + UuidToString(&uuid, &str); + memcpy(uuid_str, str, 37); + RpcStringFree(&str); + return uuid_str; +} + +void gen_uuid_inplace (char *buf) +{ + unsigned char *str = NULL; + UUID uuid; + + UuidCreate(&uuid); + UuidToString(&uuid, &str); + memcpy(buf, str, 37); + RpcStringFree(&str); +} + +gboolean +is_uuid_valid (const char *uuid_str) +{ + UUID uuid; + if (UuidFromString((unsigned char *)uuid_str, &uuid) != RPC_S_OK) + return FALSE; + return TRUE; +} + +#endif + +char** strsplit_by_space (char *string, int *length) +{ + char *remainder, *s; + int size = 8, num = 0, done = 0; + char **array; + + if (string == NULL || string[0] == '\0') { + *length = 0; + return NULL; + } + + array = malloc (sizeof(char *) * size); + + remainder = string; + while (!done) { + for (s = remainder; *s != ' ' && *s != '\0'; ++s) ; + + if (*s == '\0') + done = 1; + else + *s = '\0'; + + array[num++] = remainder; + if (!done && num == size) { + size <<= 1; + array = realloc (array, sizeof(char *) * size); + } + + remainder = s + 1; + } + + *length = num; + return array; +} + +char** strsplit_by_char (char *string, int *length, char c) +{ + char *remainder, *s; + int size = 8, num = 0, done = 0; + char **array; + + if (string == NULL || string[0] == '\0') { + *length = 0; + return NULL; + } + + array = malloc (sizeof(char *) * size); + + remainder = string; + while (!done) { + for (s = remainder; *s != c && *s != '\0'; ++s) ; + + if (*s == '\0') + done = 1; + else + *s = '\0'; + + array[num++] = remainder; + if (!done && num == size) { + size <<= 1; + array = realloc (array, sizeof(char *) * size); + } + + remainder = s + 1; + } + + *length = num; + return array; +} + +char* strjoin_n (const char *seperator, int argc, char **argv) +{ + GString *buf; + int i; + char *str; + + if (argc == 0) + return NULL; + + buf = g_string_new (argv[0]); + for (i = 1; i < argc; ++i) { + g_string_append (buf, seperator); + g_string_append (buf, argv[i]); + } + + str = buf->str; + g_string_free (buf, FALSE); + return str; +} + + +gboolean is_ipaddr_valid (const char *ip) +{ + unsigned char buf[sizeof(struct in6_addr)]; + + if (evutil_inet_pton(AF_INET, ip, buf) == 1) + return TRUE; + + if (evutil_inet_pton(AF_INET6, ip, buf) == 1) + return TRUE; + + return FALSE; +} + +void parse_key_value_pairs (char *string, KeyValueFunc func, void *data) +{ + char *line = string, *next, *space; + char *key, *value; + + while (*line) { + /* handle empty line */ + if (*line == '\n') { + ++line; + continue; + } + + for (next = line; *next != '\n' && *next; ++next) ; + *next = '\0'; + + for (space = line; space < next && *space != ' '; ++space) ; + if (*space != ' ') { + g_warning ("Bad key value format: %s\n", line); + return; + } + *space = '\0'; + key = line; + value = space + 1; + + func (data, key, value); + + line = next + 1; + } +} + +void parse_key_value_pairs2 (char *string, KeyValueFunc2 func, void *data) +{ + char *line = string, *next, *space; + char *key, *value; + + while (*line) { + /* handle empty line */ + if (*line == '\n') { + ++line; + continue; + } + + for (next = line; *next != '\n' && *next; ++next) ; + *next = '\0'; + + for (space = line; space < next && *space != ' '; ++space) ; + if (*space != ' ') { + g_warning ("Bad key value format: %s\n", line); + return; + } + *space = '\0'; + key = line; + value = space + 1; + + if (func(data, key, value) == FALSE) + break; + + line = next + 1; + } +} + +/** + * handle the empty string problem. + */ +gchar* +ccnet_key_file_get_string (GKeyFile *keyf, + const char *category, + const char *key) +{ + gchar *v; + + if (!g_key_file_has_key (keyf, category, key, NULL)) + return NULL; + + v = g_key_file_get_string (keyf, category, key, NULL); + if (v != NULL && v[0] == '\0') { + g_free(v); + return NULL; + } + + return v; +} + +/** + * string_list_is_exists: + * @str_list: + * @string: a C string or %NULL + * + * Check whether @string is in @str_list. + * + * returns: %TRUE if @string is in str_list, %FALSE otherwise + */ +gboolean +string_list_is_exists (GList *str_list, const char *string) +{ + GList *ptr; + for (ptr = str_list; ptr; ptr = ptr->next) { + if (g_strcmp0(string, ptr->data) == 0) + return TRUE; + } + return FALSE; +} + +/** + * string_list_append: + * @str_list: + * @string: a C string (can't be %NULL + * + * Append @string to @str_list if it is in the list. + * + * returns: the new start of the list + */ +GList* +string_list_append (GList *str_list, const char *string) +{ + g_return_val_if_fail (string != NULL, str_list); + + if (string_list_is_exists(str_list, string)) + return str_list; + + str_list = g_list_append (str_list, g_strdup(string)); + return str_list; +} + +GList * +string_list_append_sorted (GList *str_list, const char *string) +{ + g_return_val_if_fail (string != NULL, str_list); + + if (string_list_is_exists(str_list, string)) + return str_list; + + str_list = g_list_insert_sorted_with_data (str_list, g_strdup(string), + (GCompareDataFunc)g_strcmp0, NULL); + return str_list; +} + + +GList * +string_list_remove (GList *str_list, const char *string) +{ + g_return_val_if_fail (string != NULL, str_list); + + GList *ptr; + + for (ptr = str_list; ptr; ptr = ptr->next) { + if (strcmp((char *)ptr->data, string) == 0) { + g_free (ptr->data); + return g_list_delete_link (str_list, ptr); + } + } + return str_list; +} + + +void +string_list_free (GList *str_list) +{ + GList *ptr = str_list; + + while (ptr) { + g_free (ptr->data); + ptr = ptr->next; + } + + g_list_free (str_list); +} + + +void +string_list_join (GList *str_list, GString *str, const char *seperator) +{ + GList *ptr; + if (!str_list) + return; + + ptr = str_list; + g_string_append (str, ptr->data); + + for (ptr = ptr->next; ptr; ptr = ptr->next) { + g_string_append (str, seperator); + g_string_append (str, (char *)ptr->data); + } +} + +GList * +string_list_parse (const char *list_in_str, const char *seperator) +{ + if (!list_in_str) + return NULL; + + GList *list = NULL; + char **array = g_strsplit (list_in_str, seperator, 0); + char **ptr; + + for (ptr = array; *ptr; ptr++) { + list = g_list_prepend (list, g_strdup(*ptr)); + } + list = g_list_reverse (list); + + g_strfreev (array); + return list; +} + +GList * +string_list_parse_sorted (const char *list_in_str, const char *seperator) +{ + GList *list = string_list_parse (list_in_str, seperator); + + return g_list_sort (list, (GCompareFunc)g_strcmp0); +} + +gboolean +string_list_sorted_is_equal (GList *list1, GList *list2) +{ + GList *ptr1 = list1, *ptr2 = list2; + + while (ptr1 && ptr2) { + if (g_strcmp0(ptr1->data, ptr2->data) != 0) + break; + + ptr1 = ptr1->next; + ptr2 = ptr2->next; + } + + if (!ptr1 && !ptr2) + return TRUE; + return FALSE; +} + +char ** +ncopy_string_array (char **orig, int n) +{ + char **ret = g_malloc (sizeof(char *) * n); + int i = 0; + + for (; i < n; i++) + ret[i] = g_strdup(orig[i]); + return ret; +} + +void +nfree_string_array (char **array, int n) +{ + int i = 0; + + for (; i < n; i++) + g_free (array[i]); + g_free (array); +} + +gint64 +get_current_time() +{ + GTimeVal tv; + gint64 t; + + g_get_current_time (&tv); + t = tv.tv_sec * (gint64)1000000 + tv.tv_usec; + return t; +} + +#ifdef WIN32 +int +pgpipe (ccnet_pipe_t handles[2]) +{ + SOCKET s; + struct sockaddr_in serv_addr; + int len = sizeof( serv_addr ); + + handles[0] = handles[1] = INVALID_SOCKET; + + if ( ( s = socket( AF_INET, SOCK_STREAM, 0 ) ) == INVALID_SOCKET ) + { + g_debug("pgpipe failed to create socket: %d\n", WSAGetLastError()); + return -1; + } + + memset( &serv_addr, 0, sizeof( serv_addr ) ); + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(0); + serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + BOOL reuse = 1; + setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + + if (bind(s, (SOCKADDR *) & serv_addr, len) == SOCKET_ERROR) + { + g_debug("pgpipe failed to bind: %d\n", WSAGetLastError()); + closesocket(s); + return -1; + } + if (listen(s, 1) == SOCKET_ERROR) + { + g_debug("pgpipe failed to listen: %d\n", WSAGetLastError()); + closesocket(s); + return -1; + } + if (getsockname(s, (SOCKADDR *) & serv_addr, &len) == SOCKET_ERROR) + { + g_debug("pgpipe failed to getsockname: %d\n", WSAGetLastError()); + closesocket(s); + return -1; + } + if ((handles[1] = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) + { + g_debug("pgpipe failed to create socket 2: %d\n", WSAGetLastError()); + closesocket(s); + return -1; + } + + if (connect(handles[1], (SOCKADDR *) & serv_addr, len) == SOCKET_ERROR) + { + g_debug("pgpipe failed to connect socket: %d\n", WSAGetLastError()); + closesocket(s); + return -1; + } + if ((handles[0] = accept(s, (SOCKADDR *) & serv_addr, &len)) == INVALID_SOCKET) + { + g_debug("pgpipe failed to accept socket: %d\n", WSAGetLastError()); + closesocket(handles[1]); + handles[1] = INVALID_SOCKET; + closesocket(s); + return -1; + } + closesocket(s); + return 0; +} +#endif + +/* + The EVP_EncryptXXX and EVP_DecryptXXX series of functions have a + weird choice of returned value. +*/ +#define ENC_SUCCESS 1 +#define ENC_FAILURE 0 +#define DEC_SUCCESS 1 +#define DEC_FAILURE 0 + + +#include +#include + +/* Block size, in bytes. For AES it can only be 16 bytes. */ +#define BLK_SIZE 16 +#define ENCRYPT_BLK_SIZE BLK_SIZE +#define KEY_SIZE 32 + +int +ccnet_generate_cipher (const char *passwd, int plen, + unsigned char *key, unsigned char *iv) +{ + int key_len; + + /* Generate the derived key. We use AES 256 bits key, + CBC cipher mode, and SHA1 as the message digest + when generating the key. IV is not used in ecb mode, + actually. */ + key_len = EVP_BytesToKey (EVP_aes_256_cbc(), /* cipher mode */ + EVP_sha1(), /* message digest */ + NULL, /* salt */ + (unsigned char*)passwd, + plen, + 3, /* iteration times */ + key, /* the derived key */ + iv); /* IV, initial vector */ + + /* The key should be 32 bytes long for our 256 bit key. */ + if (key_len != KEY_SIZE) { + g_warning ("failed to init EVP_CIPHER_CTX.\n"); + return -1; + } + + return 0; +} + +int +ccnet_encrypt_with_key (char **data_out, + int *out_len, + const char *data_in, + const int in_len, + const unsigned char *key, + const unsigned char *iv) +{ + *data_out = NULL; + *out_len = -1; + + /* check validation */ + if (data_in == NULL || in_len <= 0 || + key == NULL || iv == NULL) { + g_warning ("Invalid params.\n"); + return -1; + } + + EVP_CIPHER_CTX ctx; + int ret; + int blks; + + /* Prepare CTX for encryption. */ + EVP_CIPHER_CTX_init (&ctx); + + ret = EVP_EncryptInit_ex (&ctx, + EVP_aes_256_cbc(), /* cipher mode */ + NULL, /* engine, NULL for default */ + key, /* derived key */ + iv); /* initial vector */ + + if (ret == ENC_FAILURE) { + g_warning ("error init\n"); + return -1; + } + + /* Allocating output buffer. */ + + /* + For EVP symmetric encryption, padding is always used __even if__ + data size is a multiple of block size, in which case the padding + length is the block size. so we have the following: + */ + + blks = (in_len / BLK_SIZE) + 1; + *data_out = (char *) g_malloc(blks * BLK_SIZE); + if (*data_out == NULL) { + g_warning ("failed to allocate the output buffer.\n"); + goto enc_error; + } + + int update_len, final_len; + + /* Do the encryption. */ + ret = EVP_EncryptUpdate (&ctx, + (unsigned char*)*data_out, + &update_len, + (unsigned char*)data_in, + in_len); + if (ret == ENC_FAILURE) { + g_warning ("error update\n"); + goto enc_error; + } + + /* Finish the possible partial block. */ + ret = EVP_EncryptFinal_ex (&ctx, + (unsigned char*)*data_out + update_len, + &final_len); + *out_len = update_len + final_len; + /* out_len should be equal to the allocated buffer size. */ + if (ret == ENC_FAILURE || *out_len != (blks * BLK_SIZE)) { + goto enc_error; + } + + EVP_CIPHER_CTX_cleanup (&ctx); + return 0; + +enc_error: + EVP_CIPHER_CTX_cleanup (&ctx); + *out_len = -1; + if (*data_out != NULL) + g_free (*data_out); + *data_out = NULL; + return -1; +} + +int +ccnet_decrypt_with_key (char **data_out, + int *out_len, + const char *data_in, + const int in_len, + const unsigned char *key, + const unsigned char *iv) +{ + /* Check validation. Because padding is always used, in_len must + * be a multiple of BLK_SIZE */ + if ( data_in == NULL || in_len <= 0 || in_len % BLK_SIZE != 0 || + key == NULL || iv == NULL) { + + g_warning ("Invalid param(s).\n"); + return -1; + } + + EVP_CIPHER_CTX ctx; + int ret; + + *data_out = NULL; + *out_len = -1; + + /* Prepare CTX for decryption. */ + EVP_CIPHER_CTX_init (&ctx); + ret = EVP_DecryptInit_ex (&ctx, + EVP_aes_256_cbc(), /* cipher mode */ + NULL, /* engine, NULL for default */ + key, /* derived key */ + iv); /* initial vector */ + + if (ret == DEC_FAILURE) + return -1; + + /* Allocating output buffer. */ + *data_out = (char *)g_malloc (in_len); + if (*data_out == NULL) { + g_warning ("failed to allocate the output buffer.\n"); + goto dec_error; + } + + int update_len, final_len; + + /* Do the decryption. */ + ret = EVP_DecryptUpdate (&ctx, + (unsigned char*)*data_out, + &update_len, + (unsigned char*)data_in, + in_len); + if (ret == DEC_FAILURE) + goto dec_error; + + /* Finish the possible partial block. */ + ret = EVP_DecryptFinal_ex (&ctx, + (unsigned char*)*data_out + update_len, + &final_len); + *out_len = update_len + final_len; + /* out_len should be smaller than in_len. */ + if (ret == DEC_FAILURE || *out_len > in_len) + goto dec_error; + + EVP_CIPHER_CTX_cleanup (&ctx); + return 0; + +dec_error: + EVP_CIPHER_CTX_cleanup (&ctx); + *out_len = -1; + if (*data_out != NULL) + g_free (*data_out); + *data_out = NULL; + return -1; +} + +/* convert locale specific input to utf8 encoded string */ +char *ccnet_locale_to_utf8 (const gchar *src) +{ + if (!src) + return NULL; + + gsize bytes_read = 0; + gsize bytes_written = 0; + GError *error = NULL; + gchar *dst = NULL; + + dst = g_locale_to_utf8 + (src, /* locale specific string */ + strlen(src), /* len of src */ + &bytes_read, /* length processed */ + &bytes_written, /* output length */ + &error); + + if (error) { + return NULL; + } + + return dst; +} + +/* convert utf8 input to locale specific string */ +char *ccnet_locale_from_utf8 (const gchar *src) +{ + if (!src) + return NULL; + + gsize bytes_read = 0; + gsize bytes_written = 0; + GError *error = NULL; + gchar *dst = NULL; + + dst = g_locale_from_utf8 + (src, /* locale specific string */ + strlen(src), /* len of src */ + &bytes_read, /* length processed */ + &bytes_written, /* output length */ + &error); + + if (error) { + return NULL; + } + + return dst; +} + +#ifdef WIN32 + +static HANDLE +get_process_handle (const char *process_name_in) +{ + char name[256]; + if (strstr(process_name_in, ".exe")) { + snprintf (name, sizeof(name), "%s", process_name_in); + } else { + snprintf (name, sizeof(name), "%s.exe", process_name_in); + } + + DWORD aProcesses[1024], cbNeeded, cProcesses; + + if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) + return NULL; + + /* Calculate how many process identifiers were returned. */ + cProcesses = cbNeeded / sizeof(DWORD); + + HANDLE hProcess; + HMODULE hMod; + char process_name[MAX_PATH]; + unsigned int i; + + for (i = 0; i < cProcesses; i++) { + if(aProcesses[i] == 0) + continue; + hProcess = OpenProcess (PROCESS_ALL_ACCESS, FALSE, aProcesses[i]); + if (!hProcess) + continue; + + if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded)) { + GetModuleBaseName(hProcess, hMod, process_name, + sizeof(process_name)/sizeof(char)); + } + + if (strcasecmp(process_name, name) == 0) + return hProcess; + else { + CloseHandle(hProcess); + } + } + /* Not found */ + return NULL; +} + +gboolean +process_is_running (const char *process_name) +{ + HANDLE proc_handle = get_process_handle(process_name); + + if (proc_handle) { + CloseHandle(proc_handle); + return TRUE; + } else { + return FALSE; + } +} + +int +win32_kill_process (const char *process_name) +{ + HANDLE proc_handle = get_process_handle(process_name); + + if (proc_handle) { + TerminateProcess(proc_handle, 0); + CloseHandle(proc_handle); + return 0; + } else { + return -1; + } +} + +int +win32_spawn_process (char *cmdline_in, char *working_directory) +{ + if (!cmdline_in) + return -1; + char *cmdline = g_strdup (cmdline_in); + + STARTUPINFO si; + PROCESS_INFORMATION pi; + unsigned flags; + BOOL success; + + /* we want to execute seafile without crreating a console window */ + flags = CREATE_NO_WINDOW; + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESTDHANDLES | STARTF_FORCEOFFFEEDBACK; + si.hStdInput = (HANDLE) _get_osfhandle(0); + si.hStdOutput = (HANDLE) _get_osfhandle(1); + si.hStdError = (HANDLE) _get_osfhandle(2); + + memset(&pi, 0, sizeof(pi)); + + success = CreateProcess(NULL, cmdline, NULL, NULL, TRUE, flags, + NULL, working_directory, &si, &pi); + g_free (cmdline); + if (!success) { + g_warning ("failed to fork_process: GLE=%lu\n", GetLastError()); + return -1; + } + + /* close the handle of thread so that the process object can be freed by + * system + */ + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + return 0; +} + +char * +wchar_to_utf8 (const wchar_t *wch) +{ + if (wch == NULL) { + return NULL; + } + + char *utf8 = NULL; + int bufsize, len; + + bufsize = WideCharToMultiByte + (CP_UTF8, /* multibyte code page */ + 0, /* flags */ + wch, /* src */ + -1, /* src len, -1 for all includes \0 */ + utf8, /* dst */ + 0, /* dst buf len */ + NULL, /* default char */ + NULL); /* BOOL flag indicates default char is used */ + + if (bufsize <= 0) { + g_warning ("failed to convert a string from wchar to utf8 0"); + return NULL; + } + + utf8 = g_malloc(bufsize); + len = WideCharToMultiByte + (CP_UTF8, /* multibyte code page */ + 0, /* flags */ + wch, /* src */ + -1, /* src len, -1 for all includes \0 */ + utf8, /* dst */ + bufsize, /* dst buf len */ + NULL, /* default char */ + NULL); /* BOOL flag indicates default char is used */ + + if (len != bufsize) { + g_free (utf8); + g_warning ("failed to convert a string from wchar to utf8"); + return NULL; + } + + return utf8; +} + +wchar_t * +wchar_from_utf8 (const char *utf8) +{ + if (utf8 == NULL) { + return NULL; + } + + wchar_t *wch = NULL; + int bufsize, len; + + bufsize = MultiByteToWideChar + (CP_UTF8, /* multibyte code page */ + 0, /* flags */ + utf8, /* src */ + -1, /* src len, -1 for all includes \0 */ + wch, /* dst */ + 0); /* dst buf len */ + + if (bufsize <= 0) { + g_warning ("failed to convert a string from wchar to utf8 0"); + return NULL; + } + + wch = g_malloc (bufsize * sizeof(wchar_t)); + len = MultiByteToWideChar + (CP_UTF8, /* multibyte code page */ + 0, /* flags */ + utf8, /* src */ + -1, /* src len, -1 for all includes \0 */ + wch, /* dst */ + bufsize); /* dst buf len */ + + if (len != bufsize) { + g_free (wch); + g_warning ("failed to convert a string from utf8 to wchar"); + return NULL; + } + + return wch; +} + +/* Get the commandline arguments in unicode, then convert them to utf8 */ +char ** +get_argv_utf8 (int *argc) +{ + int i = 0; + char **argv = NULL; + const wchar_t *cmdline = NULL; + wchar_t **argv_w = NULL; + + cmdline = GetCommandLineW(); + argv_w = CommandLineToArgvW (cmdline, argc); + if (!argv_w) { + printf("failed to CommandLineToArgvW(), GLE=%lu\n", GetLastError()); + return NULL; + } + + argv = (char **)malloc (sizeof(char*) * (*argc)); + for (i = 0; i < *argc; i++) { + argv[i] = wchar_to_utf8 (argv_w[i]); + } + + return argv; +} +#endif /* ifdef WIN32 */ + +#ifdef __linux__ +/* read the link of /proc/123/exe and compare with `process_name' */ +static int +find_process_in_dirent(struct dirent *dir, const char *process_name) +{ + char path[512]; + /* fisrst construct a path like /proc/123/exe */ + if (sprintf (path, "/proc/%s/exe", dir->d_name) < 0) { + return -1; + } + + char buf[PATH_MAX]; + /* get the full path of exe */ + ssize_t l = readlink(path, buf, PATH_MAX); + + if (l < 0) + return -1; + buf[l] = '\0'; + + /* get the base name of exe */ + char *base = g_path_get_basename(buf); + int ret = strcmp(base, process_name); + g_free(base); + + if (ret == 0) + return atoi(dir->d_name); + else + return -1; +} + +/* read the /proc fs to determine whether some process is running */ +gboolean process_is_running (const char *process_name) +{ + DIR *proc_dir = opendir("/proc"); + if (!proc_dir) { + fprintf (stderr, "failed to open /proc/ dir\n"); + return FALSE; + } + + struct dirent *subdir = NULL; + while ((subdir = readdir(proc_dir))) { + char first = subdir->d_name[0]; + /* /proc/[1-9][0-9]* */ + if (first > '9' || first < '1') + continue; + int pid = find_process_in_dirent(subdir, process_name); + if (pid > 0) { + closedir(proc_dir); + return TRUE; + } + } + + closedir(proc_dir); + return FALSE; +} +#endif + +#ifdef __APPLE__ +gboolean process_is_running (const char *process_name) +{ + //TODO + return FALSE; +} +#endif + +char* +ccnet_object_type_from_id (const char *object_id) +{ + char *ptr; + + if ( !(ptr = strchr(object_id, '/')) ) + return NULL; + + return g_strndup(object_id, ptr - object_id); +} + + +#ifdef WIN32 +/** + * In Win32 we need to use _stat64 for files larger than 2GB. _stat64 needs + * the `path' argument in gbk encoding. + */ + #define STAT_STRUCT struct __stat64 + #define STAT_FUNC win_stat64_utf8 + +static inline int +win_stat64_utf8 (char *path_u8, STAT_STRUCT *sb) +{ + char *path = ccnet_locale_from_utf8(path_u8); + int result = _stat64(path, sb); + g_free (path); + return result; +} + +#else + #define STAT_STRUCT struct stat + #define STAT_FUNC stat +#endif + +static gint64 +calc_recursively (const char *path, GError **calc_error) +{ + gint64 sum = 0; + + GError *error = NULL; + GDir *folder = g_dir_open(path, 0, &error); + if (!folder) { + g_set_error (calc_error, CCNET_DOMAIN, 0, + "g_open() dir %s failed:%s\n", path, error->message); + return -1; + } + + const char *name = NULL; + while ((name = g_dir_read_name(folder)) != NULL) { + STAT_STRUCT sb; + char *full_path= g_build_filename (path, name, NULL); + if (STAT_FUNC(full_path, &sb) < 0) { + g_set_error (calc_error, CCNET_DOMAIN, 0, "failed to stat on %s: %s\n", + full_path, strerror(errno)); + g_free(full_path); + g_dir_close(folder); + return -1; + } + + if (S_ISDIR(sb.st_mode)) { + gint64 size = calc_recursively(full_path, calc_error); + if (size < 0) { + g_free (full_path); + g_dir_close (folder); + return -1; + } + sum += size; + g_free(full_path); + } else if (S_ISREG(sb.st_mode)) { + sum += sb.st_size; + g_free(full_path); + } + } + + g_dir_close (folder); + return sum; +} + +gint64 +ccnet_calc_directory_size (const char *path, GError **error) +{ + return calc_recursively (path, error); +} + + +#ifdef WIN32 +/* + * strtok_r code directly from glibc.git /string/strtok_r.c since windows + * doesn't have it. + */ +char * +strtok_r(char *s, const char *delim, char **save_ptr) +{ + char *token; + + if(s == NULL) + s = *save_ptr; + + /* Scan leading delimiters. */ + s += strspn(s, delim); + if(*s == '\0') { + *save_ptr = s; + return NULL; + } + + /* Find the end of the token. */ + token = s; + s = strpbrk(token, delim); + + if(s == NULL) { + /* This token finishes the string. */ + *save_ptr = strchr(token, '\0'); + } else { + /* Terminate the token and make *SAVE_PTR point past it. */ + *s = '\0'; + *save_ptr = s + 1; + } + + return token; +} +#endif diff --git a/lib/utils.h b/lib/utils.h new file mode 100644 index 0000000..fe7b1b0 --- /dev/null +++ b/lib/utils.h @@ -0,0 +1,332 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_UTILS_H +#define CCNET_UTILS_H + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#else +#include +#endif + +#ifdef WIN32 +#include +#include + +#ifndef WEXITSTATUS +#define WEXITSTATUS(status) (((status) & 0xff00) >> 8) +#endif + +/* Borrowed from libevent */ +#define ccnet_pipe_t intptr_t + +int pgpipe (ccnet_pipe_t handles[2]); +#define ccnet_mkdir(a,b) g_mkdir((a),(b)) +#define ccnet_pipe(a) pgpipe((a)) +#define piperead(a,b,c) recv((a),(b),(c),0) +#define pipewrite(a,b,c) send((a),(b),(c),0) +#define pipeclose(a) closesocket((a)) + +static inline int ccnet_rename(const char *oldfile, const char *newfile) +{ + int ret = g_rename (oldfile, newfile); + if (ret < 0) { + if (errno != EEXIST) + return -1; + + ret = g_unlink(oldfile); + + if (ret < 0) { + g_warning("ccnet_rename failed because g_unlink failed\n"); + return -1; + } + return g_rename(oldfile, newfile); + } + return 0; +} +#else + +#define ccnet_pipe_t int + +#define ccnet_mkdir(a,b) g_mkdir((a),(b)) +#define ccnet_pipe(a) pipe((a)) +#define piperead(a,b,c) read((a),(b),(c)) +#define pipewrite(a,b,c) write((a),(b),(c)) +#define pipeclose(a) close((a)) +#define ccnet_rename g_rename +#endif + +#define pipereadn(a,b,c) recvn((a),(b),(c)) +#define pipewriten(a,b,c) sendn((a),(b),(c)) + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +/* for debug */ +#ifndef ccnet_warning +#define ccnet_warning(fmt, ...) g_warning("%s(%d): " fmt, __FILE__, __LINE__, ##__VA_ARGS__) +#endif + +#ifndef ccnet_error +#define ccnet_error(fmt, ...) g_error("%s(%d): " fmt, __FILE__, __LINE__, ##__VA_ARGS__) +#endif + +#ifndef ccnet_message +#define ccnet_message(fmt, ...) g_message("%s(%d): " fmt, __FILE__, __LINE__, ##__VA_ARGS__) +#endif + +#define CCNET_DOMAIN g_quark_from_string("ccnet") + + +struct timeval timeval_from_msec (uint64_t milliseconds); + + +size_t ccnet_strlcpy (char *dst, const char *src, size_t size); + +void rawdata_to_hex (const unsigned char *rawdata, char *hex_str, int n_bytes); +int hex_to_rawdata (const char *hex_str, unsigned char *rawdata, int n_bytes); + +#define sha1_to_hex(sha1, hex) rawdata_to_hex((sha1), (hex), 20) +#define hex_to_sha1(hex, sha1) hex_to_rawdata((hex), (sha1), 20) + +int calculate_sha1 (unsigned char *sha1, const char *msg); +int ccnet_sha1_equal (const void *v1, const void *v2); +unsigned int ccnet_sha1_hash (const void *v); + +char* gen_uuid (); +void gen_uuid_inplace (char *buf); +gboolean is_uuid_valid (const char *uuid_str); + + +/* dir operations */ +int checkdir (const char *dir); +int checkdir_with_mkdir (const char *path); +char* ccnet_expand_path (const char *src); + +/** + * Make directory with 256 sub-directories from '00' to 'ff'. + * `base` and subdir will be created if they are not existing. + */ +int objstore_mkdir (const char *base); +void objstore_get_path (char *path, const char *base, const char *obj_id); + + +char** strsplit_by_space (char *string, int *length); + +/* Read "n" bytes from a descriptor. */ +ssize_t readn(int fd, void *vptr, size_t n); +ssize_t writen(int fd, const void *vptr, size_t n); + +/* Read "n" bytes from a socket. */ +ssize_t recvn(evutil_socket_t fd, void *vptr, size_t n); +ssize_t sendn(evutil_socket_t fd, const void *vptr, size_t n); + +int copy_fd (int ifd, int ofd); +int copy_file (const char *dst, const char *src, int mode); + + +/* string utilities */ + +char** strsplit_by_char (char *string, int *length, char c); + +char * strjoin_n (const char *seperator, int argc, char **argv); + +int is_ipaddr_valid (const char *ip); + +typedef void (*KeyValueFunc) (void *data, const char *key, char *value); +void parse_key_value_pairs (char *string, KeyValueFunc func, void *data); + +typedef gboolean (*KeyValueFunc2) (void *data, const char *key, + const char *value); +void parse_key_value_pairs2 (char *string, KeyValueFunc2 func, void *data); + +gchar* ccnet_key_file_get_string (GKeyFile *keyf, + const char *category, + const char *key); + + +GList *string_list_append (GList *str_list, const char *string); +GList *string_list_append_sorted (GList *str_list, const char *string); +GList *string_list_remove (GList *str_list, const char *string); +void string_list_free (GList *str_list); +gboolean string_list_is_exists (GList *str_list, const char *string); +void string_list_join (GList *str_list, GString *strbuf, const char *seperator); +GList *string_list_parse (const char *list_in_str, const char *seperator); +GList *string_list_parse_sorted (const char *list_in_str, const char *seperator); +gboolean string_list_sorted_is_equal (GList *list1, GList *list2); + +char** ncopy_string_array (char **orig, int n); +void nfree_string_array (char **array, int n); + +/* 64bit time */ +gint64 get_current_time(); + +int +ccnet_generate_cipher (const char *passwd, int plen, + unsigned char *key, unsigned char *iv); + +int +ccnet_encrypt_with_key (char **data_out, + int *out_len, + const char *data_in, + const int in_len, + const unsigned char *key, + const unsigned char *iv); + +int +ccnet_decrypt_with_key (char **data_out, + int *out_len, + const char *data_in, + const int in_len, + const unsigned char *key, + const unsigned char *iv); + +int +ccnet_encrypt (char **data_out, + int *out_len, + const char *data_in, + const int in_len, + const char *code, + const int code_len); + + +int +ccnet_decrypt (char **data_out, + int *out_len, + const char *data_in, + const int in_len, + const char *code, + const int code_len); + + +/* + * Utility functions for converting data to/from network byte order. + */ + +static inline uint64_t +hton64(uint64_t val) +{ + uint64_t ret; + uint8_t *ptr = (uint8_t *)&ret; + + ptr[0]=((val)>>56)&0xFF; + ptr[1]=((val)>>48)&0xFF; + ptr[2]=((val)>>40)&0xFF; + ptr[3]=((val)>>32)&0xFF; + ptr[4]=((val)>>24)&0xFF; + ptr[5]=((val)>>16)&0xFF; + ptr[6]=((val)>>8)&0xFF; + ptr[7]=(val)&0xFF; + + return ret; +} + +static inline uint64_t +ntoh64(uint64_t val) +{ + uint64_t t64; + uint8_t *ptr = (uint8_t *)&val; + + t64=(ptr[3]+256*(ptr[2]+256*(ptr[1]+256*ptr[0]))); + t64<<=32; + t64|=((ptr[7]+256*(ptr[6]+256*(ptr[5]+256*ptr[4]))))&0xffffffffU; + + return t64; +} + +static inline void put64bit(uint8_t **ptr,uint64_t val) { + (*ptr)[0]=((val)>>56)&0xFF; + (*ptr)[1]=((val)>>48)&0xFF; + (*ptr)[2]=((val)>>40)&0xFF; + (*ptr)[3]=((val)>>32)&0xFF; + (*ptr)[4]=((val)>>24)&0xFF; + (*ptr)[5]=((val)>>16)&0xFF; + (*ptr)[6]=((val)>>8)&0xFF; + (*ptr)[7]=(val)&0xFF; + (*ptr)+=8; +} + +static inline void put32bit(uint8_t **ptr,uint32_t val) { + (*ptr)[0]=((val)>>24)&0xFF; + (*ptr)[1]=((val)>>16)&0xFF; + (*ptr)[2]=((val)>>8)&0xFF; + (*ptr)[3]=(val)&0xFF; + (*ptr)+=4; +} + +static inline void put16bit(uint8_t **ptr,uint16_t val) { + (*ptr)[0]=((val)>>8)&0xFF; + (*ptr)[1]=(val)&0xFF; + (*ptr)+=2; +} + +static inline void put8bit(uint8_t **ptr,uint8_t val) { + (*ptr)[0]=(val)&0xFF; + (*ptr)++; +} + +static inline uint64_t get64bit(const uint8_t **ptr) { + uint64_t t64; + t64=((*ptr)[3]+256*((*ptr)[2]+256*((*ptr)[1]+256*(*ptr)[0]))); + t64<<=32; + t64|=(((*ptr)[7]+256*((*ptr)[6]+256*((*ptr)[5]+256*(*ptr)[4]))))&0xffffffffU; + (*ptr)+=8; + return t64; +} + +static inline uint32_t get32bit(const uint8_t **ptr) { + uint32_t t32; + t32=((*ptr)[3]+256*((*ptr)[2]+256*((*ptr)[1]+256*(*ptr)[0]))); + (*ptr)+=4; + return t32; +} + +static inline uint16_t get16bit(const uint8_t **ptr) { + uint32_t t16; + t16=(*ptr)[1]+256*(*ptr)[0]; + (*ptr)+=2; + return t16; +} + +static inline uint8_t get8bit(const uint8_t **ptr) { + uint32_t t8; + t8=(*ptr)[0]; + (*ptr)++; + return t8; +} + +/* Convert between local encoding and utf8. Returns the converted + * string if success, otherwise return NULL + */ +char *ccnet_locale_from_utf8 (const gchar *src); +char *ccnet_locale_to_utf8 (const gchar *src); + +/* Detect whether a process with the given name is running right now. */ +gboolean process_is_running(const char *name); + +#ifdef WIN32 +int win32_kill_process (const char *process_name_in); +int win32_spawn_process (char *cmd, char *wd); +char *wchar_to_utf8 (const wchar_t *src); +wchar_t *wchar_from_utf8 (const char *src); +char **get_argv_utf8 (int *argc); +#endif + +char* ccnet_object_type_from_id (const char *object_id); + +gint64 ccnet_calc_directory_size (const char *path, GError **error); + +#ifdef WIN32 +char * strtok_r(char *s, const char *delim, char **save_ptr); +#endif + +#endif diff --git a/libccnet.pc.in b/libccnet.pc.in new file mode 100644 index 0000000..f452ad9 --- /dev/null +++ b/libccnet.pc.in @@ -0,0 +1,11 @@ +prefix=(DESTDIR)@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libccnet +Description: Client library for accessing ccnet service. +Version: @VERSION@ +Libs: -L${libdir} -lccnet @SEARPC_LIBS@ @LIBEVENT_LIBS@ @LIB_UUID@ +Cflags: -I${includedir} @SEARPC_CFLAGS@ @LIBEVENT_CFLAGS@ +Requires: gobject-2.0 glib-2.0 diff --git a/m4/python.m4 b/m4/python.m4 new file mode 100644 index 0000000..fe90156 --- /dev/null +++ b/m4/python.m4 @@ -0,0 +1,66 @@ +## this one is commonly used with AM_PATH_PYTHONDIR ... +dnl AM_CHECK_PYMOD(MODNAME [,SYMBOL [,ACTION-IF-FOUND [,ACTION-IF-NOT-FOUND]]]) +dnl Check if a module containing a given symbol is visible to python. +AC_DEFUN([AM_CHECK_PYMOD], +[AC_REQUIRE([AM_PATH_PYTHON]) +py_mod_var=`echo $1['_']$2 | sed 'y%./+-%__p_%'` +AC_MSG_CHECKING(for ifelse([$2],[],,[$2 in ])python module $1) +AC_CACHE_VAL(py_cv_mod_$py_mod_var, [ +ifelse([$2],[], [prog=" +import sys +try: + import $1 +except ImportError: + sys.exit(1) +except: + sys.exit(0) +sys.exit(0)"], [prog=" +import $1 +$1.$2"]) +if $PYTHON -c "$prog" 1>&AC_FD_CC 2>&AC_FD_CC + then + eval "py_cv_mod_$py_mod_var=yes" + else + eval "py_cv_mod_$py_mod_var=no" + fi +]) +py_val=`eval "echo \`echo '$py_cv_mod_'$py_mod_var\`"` +if test "x$py_val" != xno; then + AC_MSG_RESULT(yes) + ifelse([$3], [],, [$3 +])dnl +else + AC_MSG_RESULT(no) + ifelse([$4], [],, [$4 +])dnl +fi +]) + +dnl a macro to check for ability to create python extensions +dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE]) +dnl function also defines PYTHON_INCLUDES +AC_DEFUN([AM_CHECK_PYTHON_HEADERS], +[AC_REQUIRE([AM_PATH_PYTHON]) +AC_MSG_CHECKING(for headers required to compile python extensions) +dnl deduce PYTHON_INCLUDES +py_prefix=`$PYTHON -c "import sys; print sys.prefix"` +py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"` +if test -x "$PYTHON-config"; then +PYTHON_INCLUDES=`$PYTHON-config --includes 2>/dev/null` +else +PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}" +if test "$py_prefix" != "$py_exec_prefix"; then + PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}" +fi +fi +AC_SUBST(PYTHON_INCLUDES) +dnl check if the headers exist: +save_CPPFLAGS="$CPPFLAGS" +CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES" +AC_TRY_CPP([#include ],dnl +[AC_MSG_RESULT(found) +$1],dnl +[AC_MSG_RESULT(not found) +$2]) +CPPFLAGS="$save_CPPFLAGS" +]) diff --git a/net/Makefile.am b/net/Makefile.am new file mode 100644 index 0000000..d2499c6 --- /dev/null +++ b/net/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = common server diff --git a/net/common/Makefile.am b/net/common/Makefile.am new file mode 100644 index 0000000..3588269 --- /dev/null +++ b/net/common/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = db-wrapper diff --git a/net/common/algorithms.c b/net/common/algorithms.c new file mode 100644 index 0000000..6c138fb --- /dev/null +++ b/net/common/algorithms.c @@ -0,0 +1,124 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "peer.h" +#include "session.h" +#include "peer-mgr.h" + +#include "message.h" +#include "message-manager.h" + +#include "proc-factory.h" +#include "processors/sendmsg-proc.h" + +#include "algorithms.h" + +#define DEBUG_FLAG CCNET_DEBUG_MESSAGE +#include "log.h" + + +#if 0 +static void notify_sendmsg_success (CcnetSession *session, CcnetMessage *ack_msg, + char *destid) +{ + CcnetMessageManager *msg_mgr = session->msg_mgr; + CcnetMessage *msg; + char buf[128] = {0}; + + g_assert (ack_msg != NULL); + snprintf (buf, sizeof(buf), "%s %s", ack_msg->id, destid); + msg = ccnet_message_new (session->base.id, ack_msg->from, ack_msg->app, + buf, FLAG_IS_ACK); + if (memcmp(session->base.id, ack_msg->from, 40) == 0) + ccnet_message_manager_add_msg (msg_mgr, msg, MSG_TYPE_SYS); + else + ccnet_send_message (session, msg); +} + +static void msg_cb (CcnetProcessor *proc, gboolean success, void *data) +{ + CcnetMessage *msg = data; + if (success) + notify_sendmsg_success (proc->session, msg, proc->peer->id); +} +#endif /* 0 */ + +static void +ccnet_send_msg_to_peer (CcnetSession *session, CcnetPeer *peer, + CcnetMessage *msg) +{ + CcnetProcessor *processor; + CcnetProcFactory *factory = session->proc_factory; + + if (peer->net_state == PEER_CONNECTED) { + processor = ccnet_proc_factory_create_master_processor + (factory, "send-msg", peer); + + ccnet_sendmsg_proc_set_msg (CCNET_SENDMSG_PROC(processor), msg); + /* g_signal_connect (processor, "done", */ + /* G_CALLBACK(msg_cb), msg); */ + ccnet_processor_start (processor, 0, NULL); + } +} + +void +ccnet_send_message (CcnetSession *session, CcnetMessage *msg) +{ + CcnetPeer *peer; + + peer = ccnet_peer_manager_get_peer (session->peer_mgr, msg->to); + if (peer) { + if (peer->is_self) + ccnet_message_manager_add_msg (session->msg_mgr, + msg, MSG_TYPE_RECV); + else + ccnet_send_msg_to_peer (session, peer, msg); + + g_object_unref (peer); + } else + g_warning ("Invalid peer id %s\n", msg->to); +} + + +static gboolean +check_message_permission (CcnetSession *session, CcnetMessage *msg) +{ + CcnetPeerManager *peer_mgr = session->peer_mgr; + CcnetPeer *from; + + if (strcmp(msg->app, IPEERMGR_APP) == 0) + return TRUE; + + if (msg->flags & FLAG_IS_ACK) + return TRUE; + + from = ccnet_peer_manager_get_peer (peer_mgr, msg->from); + if (!from) + return FALSE; + /* peer to peer message */ + /* TODO: improve performance */ + if (ccnet_peer_has_role(from, "MyPeer") || + ccnet_peer_has_role(from, "MyRelay") || + ccnet_peer_has_role(from, "MyClient")) { + g_object_unref (from); + return TRUE; + } + g_object_unref (from); + + return FALSE; +} + + +int +ccnet_recv_message (CcnetSession *session, CcnetMessage *msg) +{ + CcnetMessageManager *msg_mgr = session->msg_mgr; + + if (check_message_permission(session, msg) == FALSE) + return -1; + + ccnet_message_manager_add_msg (msg_mgr, msg, MSG_TYPE_RECV); + + return 0; +} diff --git a/net/common/algorithms.h b/net/common/algorithms.h new file mode 100644 index 0000000..fa4be0f --- /dev/null +++ b/net/common/algorithms.h @@ -0,0 +1,10 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_ALGORITHMS_H +#define CCNET_ALGORITHMS_H + +void ccnet_send_message (CcnetSession *session, CcnetMessage *msg); +int ccnet_recv_message (CcnetSession *session, CcnetMessage *msg); + + +#endif diff --git a/net/common/ccnet-config.c b/net/common/ccnet-config.c new file mode 100644 index 0000000..5504fb8 --- /dev/null +++ b/net/common/ccnet-config.c @@ -0,0 +1,114 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "ccnet-config.h" + +static gboolean +get_value (sqlite3_stmt *stmt, void *data) +{ + char **p_value = data; + + *p_value = g_strdup((char *) sqlite3_column_text (stmt, 0)); + /* Only one result. */ + return FALSE; +} + +static char * +config_get_string (sqlite3 *config_db, const char *key) +{ + char sql[256]; + char *value = NULL; + + snprintf (sql, sizeof(sql), + "SELECT value FROM Config WHERE key='%s';", + key); + if (sqlite_foreach_selected_row (config_db, sql, + get_value, &value) < 0) + return NULL; + + return value; +} + +char * +ccnet_session_config_get_string (CcnetSession *session, + const char *key) +{ + return (config_get_string (session->config_db, key)); +} + +int +ccnet_session_config_get_int (CcnetSession *session, + const char *key, + gboolean *exists) +{ + char *value; + int ret; + + value = config_get_string (session->config_db, key); + if (!value) { + *exists = FALSE; + return -1; + } + + *exists = TRUE; + ret = atoi (value); + g_free (value); + return ret; +} + +int +ccnet_session_config_set_string (CcnetSession *session, + const char *key, + const char *value) +{ + char sql[256]; + + snprintf (sql, sizeof(sql), + "REPLACE INTO Config VALUES ('%s', '%s');", + key, value); + if (sqlite_query_exec (session->config_db, sql) < 0) + return -1; + + return 0; +} + +int +ccnet_session_config_set_int (CcnetSession *session, + const char *key, + int value) +{ + char sql[256]; + + snprintf (sql, sizeof(sql), + "REPLACE INTO Config VALUES ('%s', %d);", + key, value); + if (sqlite_query_exec (session->config_db, sql) < 0) + return -1; + + return 0; +} + +sqlite3 * +ccnet_session_config_open_db (const char *ccnet_dir) +{ + sqlite3 *db; + char *db_path; + + db_path = g_build_filename (ccnet_dir, "config.db", NULL); + if (sqlite_open_db (db_path, &db) < 0) + return NULL; + g_free (db_path); + + /* + * Values are stored in text. You should convert it + * back to integer if needed when you read it from + * db. + */ + char *sql = "CREATE TABLE IF NOT EXISTS Config (" + "key TEXT PRIMARY KEY, " + "value TEXT);"; + sqlite_query_exec (db, sql); + + return db; +} diff --git a/net/common/ccnet-config.h b/net/common/ccnet-config.h new file mode 100644 index 0000000..a64a8a7 --- /dev/null +++ b/net/common/ccnet-config.h @@ -0,0 +1,39 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef SEAFILE_CONFIG_H +#define SEAFILE_CONFIG_H + +#include "session.h" +#include "db.h" + +/* + * Returns: config value in string. The string should be freed by caller. + */ +char * +ccnet_session_config_get_string (CcnetSession *session, + const char *key); + +/* + * Returns: + * If key exists, @exists will be set to TRUE and returns the value; + * otherwise, @exists will be set to FALSE and returns -1. + */ +int +ccnet_session_config_get_int (CcnetSession *session, + const char *key, + gboolean *exists); + +int +ccnet_session_config_set_string (CcnetSession *session, + const char *key, + const char *value); + +int +ccnet_session_config_set_int (CcnetSession *session, + const char *key, + int value); + +sqlite3 * +ccnet_session_config_open_db (const char *ccnet_dir); + +#endif diff --git a/net/common/ccnet-db.c b/net/common/ccnet-db.c new file mode 100644 index 0000000..4844024 --- /dev/null +++ b/net/common/ccnet-db.c @@ -0,0 +1,973 @@ + +#include "common.h" + +#include "log.h" + +#include "db-wrapper/db-wrapper.h" +#include "ccnet-db.h" + +#define MAX_GET_CONNECTION_RETRIES 3 + +struct CcnetDB { + int type; + DBConnPool *pool; +}; + +struct CcnetDBRow { + ResultSet *res; +}; + +struct CcnetDBTrans { + DBConnection *conn; +}; + +CcnetDB * +ccnet_db_new_mysql (const char *host, + int port, + const char *user, + const char *passwd, + const char *db_name, + const char *unix_socket, + gboolean use_ssl, + const char *charset, + int max_connections) +{ + CcnetDB *db; + + db = g_new0 (CcnetDB, 1); + if (!db) { + ccnet_warning ("Failed to alloc db structure.\n"); + return NULL; + } + + db->type = CCNET_DB_TYPE_MYSQL; + + db->pool = db_conn_pool_new_mysql (host, user, passwd, port, db_name, + unix_socket, use_ssl, charset, max_connections); + + return db; +} + +CcnetDB * +ccnet_db_new_pgsql (const char *host, + const char *user, + const char *passwd, + const char *db_name, + const char *unix_socket) +{ + CcnetDB *db; + + db = g_new0 (CcnetDB, 1); + if (!db) { + ccnet_warning ("Failed to alloc db structure.\n"); + return NULL; + } + + db->type = CCNET_DB_TYPE_PGSQL; + + db->pool = db_conn_pool_new_pgsql (host, user, passwd, db_name, unix_socket); + + return db; +} + +CcnetDB * +ccnet_db_new_sqlite (const char *db_path) +{ + CcnetDB *db; + + db = g_new0 (CcnetDB, 1); + if (!db) { + ccnet_warning ("Failed to alloc db structure.\n"); + return NULL; + } + + db->type = CCNET_DB_TYPE_SQLITE; + + db->pool = db_conn_pool_new_sqlite (db_path, 0); + + return db; +} + +void +ccnet_db_free (CcnetDB *db) +{ + db_conn_pool_free (db->pool); + g_free (db); +} + +int +ccnet_db_type (CcnetDB *db) +{ + return db->type; +} + +static DBConnection * +get_db_connection (CcnetDB *db) +{ + DBConnection *conn; + GError *error = NULL; + + conn = db_conn_pool_get_connection (db->pool, &error); + + if (!conn) { + ccnet_warning ("Failed to get database connection: %s.\n", error->message); + g_clear_error (&error); + } + + return conn; +} + +int +ccnet_db_query (CcnetDB *db, const char *sql) +{ + GError *error = NULL; + int ret = 0; + + DBConnection *conn = get_db_connection (db); + if (!conn) + return -1; + + if (!db_connection_execute (conn, sql, &error)) { + ccnet_warning ("Error exec query %s: %s.\n", sql, error->message); + g_clear_error (&error); + ret = -1; + } + + db_connection_close (conn); + + return ret; +} + +gboolean +ccnet_db_check_for_existence (CcnetDB *db, const char *sql, gboolean *db_err) +{ + DBConnection *conn; + ResultSet *result; + gboolean ret = TRUE; + GError *error = NULL; + + *db_err = FALSE; + + conn = get_db_connection (db); + if (!conn) { + *db_err = TRUE; + return FALSE; + } + + result = db_connection_execute_query (conn, sql, &error); + if (error) { + ccnet_warning ("Error exec query %s: %s.\n", sql, error->message); + g_clear_error (&error); + *db_err = TRUE; + ret = FALSE; + goto out; + } + + ret = result_set_next (result, &error); + if (error) { + ccnet_warning ("Error exec query %s: %s.\n", sql, error->message); + g_clear_error (&error); + *db_err = TRUE; + } + +out: + db_connection_close (conn); + + return ret; +} + +int +ccnet_db_foreach_selected_row (CcnetDB *db, const char *sql, + CcnetDBRowFunc callback, void *data) +{ + DBConnection *conn; + ResultSet *result; + CcnetDBRow ccnet_row; + int n_rows = 0; + GError *error = NULL; + + conn = get_db_connection (db); + if (!conn) + return -1; + + result = db_connection_execute_query (conn, sql, &error); + if (error) { + ccnet_warning ("Error exec query %s: %s.\n", sql, error->message); + g_clear_error (&error); + n_rows = -1; + goto out; + } + + ccnet_row.res = result; + + while (result_set_next (result, &error)) { + n_rows++; + if (!callback (&ccnet_row, data)) + break; + } + + if (error) { + ccnet_warning ("Error exec query %s: %s.\n", sql, error->message); + g_clear_error (&error); + n_rows = -1; + } + +out: + db_connection_close (conn); + return n_rows; +} + +const char * +ccnet_db_row_get_column_text (CcnetDBRow *row, guint32 idx) +{ + GError *error = NULL; + g_return_val_if_fail (idx < result_set_get_column_count(row->res), NULL); + + return result_set_get_string (row->res, idx, &error); +} + +int +ccnet_db_row_get_column_int (CcnetDBRow *row, guint32 idx) +{ + GError *error = NULL; + g_return_val_if_fail (idx < result_set_get_column_count(row->res), -1); + + return result_set_get_int (row->res, idx, &error); +} + +gint64 +ccnet_db_row_get_column_int64 (CcnetDBRow *row, guint32 idx) +{ + GError *error = NULL; + g_return_val_if_fail (idx < result_set_get_column_count(row->res), -1); + + return result_set_get_int64 (row->res, idx, &error); +} + +int +ccnet_db_get_int (CcnetDB *db, const char *sql) +{ + int ret = -1; + DBConnection *conn; + ResultSet *result; + CcnetDBRow ccnet_row; + GError *error = NULL; + + conn = get_db_connection (db); + if (!conn) + return -1; + + result = db_connection_execute_query (conn, sql, &error); + if (error) { + ccnet_warning ("Error exec query %s: %s.\n", sql, error->message); + g_clear_error (&error); + goto out; + } + + ccnet_row.res = result; + if (!result_set_next (result, &error)) { + if (error) { + ccnet_warning ("Error exec query %s: %s.\n", sql, error->message); + g_clear_error (&error); + } + goto out; + } + + ret = ccnet_db_row_get_column_int (&ccnet_row, 0); + +out: + db_connection_close (conn); + return ret; +} + +gint64 +ccnet_db_get_int64 (CcnetDB *db, const char *sql) +{ + gint64 ret = -1; + DBConnection *conn; + ResultSet *result; + CcnetDBRow ccnet_row; + GError *error = NULL; + + conn = get_db_connection (db); + if (!conn) + return -1; + + result = db_connection_execute_query (conn, sql, &error); + if (error) { + ccnet_warning ("Error exec query %s: %s.\n", sql, error->message); + g_clear_error (&error); + goto out; + } + + ccnet_row.res = result; + if (!result_set_next (result, &error)) { + if (error) { + ccnet_warning ("Error exec query %s: %s.\n", sql, error->message); + g_clear_error (&error); + } + goto out; + } + + ret = ccnet_db_row_get_column_int64 (&ccnet_row, 0); + +out: + db_connection_close (conn); + return ret; +} + +char * +ccnet_db_get_string (CcnetDB *db, const char *sql) +{ + char *ret = NULL; + const char *s; + DBConnection *conn; + ResultSet *result; + CcnetDBRow ccnet_row; + GError *error = NULL; + + conn = get_db_connection (db); + if (!conn) + return NULL; + + result = db_connection_execute_query (conn, sql, &error); + if (error) { + ccnet_warning ("Error exec query %s: %s.\n", sql, error->message); + g_clear_error (&error); + goto out; + } + + ccnet_row.res = result; + if (!result_set_next (result, &error)) { + if (error) { + ccnet_warning ("Error exec query %s: %s.\n", sql, error->message); + g_clear_error (&error); + } + goto out; + } + + s = ccnet_db_row_get_column_text (&ccnet_row, 0); + ret = g_strdup(s); + +out: + db_connection_close (conn); + return ret; +} + +char * +ccnet_db_escape_string (CcnetDB *db, const char *from) +{ + const char *p = from; + char *to, *q; + + to = g_malloc0 (2*strlen(from)+1); + q = to; + + while (*p != '\0') { + if (*p == '\'' || *p == '\\' || *p == '"') { + *q = *p; + *(++q) = *p; + } else + *q = *p; + ++p; + ++q; + } + + return to; +} + +gboolean +pgsql_index_exists (CcnetDB *db, const char *index_name) +{ + char sql[256]; + gboolean db_err = FALSE; + + snprintf (sql, sizeof(sql), + "SELECT 1 FROM pg_class WHERE relname='%s'", + index_name); + return ccnet_db_check_for_existence (db, sql, &db_err); +} + +/* Prepared Statements */ + +struct CcnetDBStatement { + DBStmt *p; + DBConnection *conn; +}; +typedef struct CcnetDBStatement CcnetDBStatement; + +CcnetDBStatement * +ccnet_db_prepare_statement (CcnetDB *db, const char *sql) +{ + DBStmt *p; + CcnetDBStatement *ret = g_new0 (CcnetDBStatement, 1); + GError *error = NULL; + + DBConnection *conn = get_db_connection (db); + if (!conn) { + g_free (ret); + return NULL; + } + + p = db_connection_prepare_statement (conn, sql, &error); + if (!p) { + ccnet_warning ("Error prepare statement %s: %s.\n", sql, error->message); + g_clear_error (&error); + g_free (ret); + db_connection_close (conn); + return NULL; + } + + ret->p = p; + ret->conn = conn; + + return ret; +} + +void +ccnet_db_statement_free (CcnetDBStatement *p) +{ + db_connection_close (p->conn); + g_free (p); +} + +int +ccnet_db_statement_set_int (DBStmt *p, int idx, int x) +{ + GError *error = NULL; + + if (!db_stmt_set_int (p, idx, x, &error)) { + ccnet_warning ("Error set int in prep stmt: %s.\n", error->message); + g_clear_error (&error); + return -1; + } + + return 0; +} + +int +ccnet_db_statement_set_string (DBStmt *p, int idx, const char *s) +{ + GError *error = NULL; + + if (!db_stmt_set_string (p, idx, s, &error)) { + ccnet_warning ("Error set string in prep stmt: %s.\n", error->message); + g_clear_error (&error); + return -1; + } + + return 0; +} + +int +ccnet_db_statement_set_int64 (DBStmt *p, int idx, gint64 x) +{ + GError *error = NULL; + + if (!db_stmt_set_int64 (p, idx, x, &error)) { + ccnet_warning ("Error set int64 in prep stmt: %s.\n", error->message); + g_clear_error (&error); + return -1; + } + + return 0; +} + +static int +set_parameters_va (DBStmt *p, int n, va_list args) +{ + int i; + const char *type; + + for (i = 0; i < n; ++i) { + type = va_arg (args, const char *); + if (strcmp(type, "int") == 0) { + int x = va_arg (args, int); + if (ccnet_db_statement_set_int (p, i, x) < 0) + return -1; + } else if (strcmp (type, "int64") == 0) { + gint64 x = va_arg (args, gint64); + if (ccnet_db_statement_set_int64 (p, i, x) < 0) + return -1; + } else if (strcmp (type, "string") == 0) { + const char *s = va_arg (args, const char *); + if (ccnet_db_statement_set_string (p, i, s) < 0) + return -1; + } else { + ccnet_warning ("BUG: invalid prep stmt parameter type %s.\n", type); + g_return_val_if_reached (-1); + } + } + + return 0; +} + +int +ccnet_db_statement_query (CcnetDB *db, const char *sql, int n, ...) +{ + CcnetDBStatement *p; + int ret = 0; + GError *error = NULL; + + p = ccnet_db_prepare_statement (db, sql); + if (!p) + return -1; + + va_list args; + va_start (args, n); + if (set_parameters_va (p->p, n, args) < 0) { + ccnet_db_statement_free (p); + va_end (args); + return -1; + } + va_end (args); + + if (!db_stmt_execute (p->p, &error)) { + ccnet_warning ("Error execute prep stmt: %s.\n", error->message); + g_clear_error (&error); + ret = -1; + } + + ccnet_db_statement_free (p); + return ret; +} + +gboolean +ccnet_db_statement_exists (CcnetDB *db, const char *sql, int n, ...) +{ + CcnetDBStatement *p; + ResultSet *result; + gboolean ret = TRUE; + GError *error = NULL; + + p = ccnet_db_prepare_statement (db, sql); + if (!p) { + return FALSE; + } + + va_list args; + va_start (args, n); + if (set_parameters_va (p->p, n, args) < 0) { + ccnet_db_statement_free (p); + va_end (args); + return FALSE; + } + va_end (args); + + result = db_stmt_execute_query (p->p, &error); + if (error) { + ccnet_warning ("Error exec prep stmt: %s.\n", error->message); + g_clear_error (&error); + ret = FALSE; + goto out; + } + + if (!result_set_next (result, &error)) + ret = FALSE; + + if (error) { + ccnet_warning ("Error get next result from prep stmt: %s.\n", error->message); + g_clear_error (&error); + } + +out: + ccnet_db_statement_free (p); + return ret; +} + +int +ccnet_db_statement_foreach_row (CcnetDB *db, + const char *sql, + CcnetDBRowFunc callback, void *data, + int n, ...) +{ + CcnetDBStatement *p; + ResultSet *result; + CcnetDBRow ccnet_row; + int n_rows = 0; + GError *error = NULL; + + p = ccnet_db_prepare_statement (db, sql); + if (!p) + return -1; + + va_list args; + va_start (args, n); + if (set_parameters_va (p->p, n, args) < 0) { + ccnet_db_statement_free (p); + va_end (args); + return -1; + } + va_end (args); + + result = db_stmt_execute_query (p->p, &error); + if (error) { + ccnet_warning ("Error exec prep stmt: %s.\n", error->message); + g_clear_error (&error); + n_rows = -1; + goto out; + } + + ccnet_row.res = result; + + while (result_set_next (result, &error)) { + n_rows++; + if (!callback (&ccnet_row, data)) + break; + } + + if (error) { + ccnet_warning ("Error get next result from prep stmt: %s.\n", error->message); + g_clear_error (&error); + n_rows = -1; + } + +out: + ccnet_db_statement_free (p); + return n_rows; +} + +int +ccnet_db_statement_get_int (CcnetDB *db, const char *sql, int n, ...) +{ + CcnetDBStatement *p; + int ret = -1; + ResultSet *result; + CcnetDBRow ccnet_row; + GError *error = NULL; + + p = ccnet_db_prepare_statement (db, sql); + if (!p) + return -1; + + va_list args; + va_start (args, n); + if (set_parameters_va (p->p, n, args) < 0) { + ccnet_db_statement_free (p); + va_end (args); + return -1; + } + va_end (args); + + result = db_stmt_execute_query (p->p, &error); + if (error) { + ccnet_warning ("Error exec prep stmt: %s.\n", error->message); + g_clear_error (&error); + goto out; + } + + ccnet_row.res = result; + if (!result_set_next (result, &error)) { + if (error) { + ccnet_warning ("Error get next result from prep stmt: %s.\n", error->message); + g_clear_error (&error); + } + goto out; + } + + ret = ccnet_db_row_get_column_int (&ccnet_row, 0); + +out: + ccnet_db_statement_free (p); + return ret; +} + +gint64 +ccnet_db_statement_get_int64 (CcnetDB *db, const char *sql, int n, ...) +{ + CcnetDBStatement *p; + gint64 ret = -1; + ResultSet *result; + CcnetDBRow ccnet_row; + GError *error = NULL; + + p = ccnet_db_prepare_statement (db, sql); + if (!p) + return -1; + + va_list args; + va_start (args, n); + if (set_parameters_va (p->p, n, args) < 0) { + ccnet_db_statement_free (p); + va_end (args); + return -1; + } + va_end (args); + + result = db_stmt_execute_query (p->p, &error); + if (error) { + ccnet_warning ("Error exec prep stmt: %s.\n", error->message); + g_clear_error (&error); + goto out; + } + + ccnet_row.res = result; + if (!result_set_next (result, &error)) { + if (error) { + ccnet_warning ("Error get next result from prep stmt: %s.\n", error->message); + g_clear_error (&error); + } + goto out; + } + + ret = ccnet_db_row_get_column_int64 (&ccnet_row, 0); + +out: + ccnet_db_statement_free (p); + return ret; +} + +char * +ccnet_db_statement_get_string (CcnetDB *db, const char *sql, int n, ...) +{ + CcnetDBStatement *p; + char *ret = NULL; + const char *s; + ResultSet *result; + CcnetDBRow ccnet_row; + GError *error = NULL; + + p = ccnet_db_prepare_statement (db, sql); + if (!p) + return NULL; + + va_list args; + va_start (args, n); + if (set_parameters_va (p->p, n, args) < 0) { + ccnet_db_statement_free (p); + va_end (args); + return NULL; + } + va_end (args); + + result = db_stmt_execute_query (p->p, &error); + if (error) { + ccnet_warning ("Error exec prep stmt: %s.\n", error->message); + g_clear_error (&error); + goto out; + } + + ccnet_row.res = result; + if (!result_set_next (result, &error)) { + if (error) { + ccnet_warning ("Error get next result from prep stmt: %s.\n", error->message); + g_clear_error (&error); + } + goto out; + } + + s = ccnet_db_row_get_column_text (&ccnet_row, 0); + ret = g_strdup(s); + +out: + ccnet_db_statement_free (p); + return ret; +} + +/* Transaction */ + +CcnetDBTrans * +ccnet_db_begin_transaction (CcnetDB *db) +{ + DBConnection *conn; + CcnetDBTrans *trans; + GError *error = NULL; + + trans = g_new0 (CcnetDBTrans, 1); + if (!trans) + return NULL; + + conn = get_db_connection (db); + if (!conn) { + g_free (trans); + return NULL; + } + + trans->conn = conn; + if (!db_connection_begin_transaction (trans->conn, &error)) { + ccnet_warning ("Start transaction failed: %s.\n", error->message); + g_clear_error (&error); + db_connection_close (trans->conn); + g_free (trans); + return NULL; + } + + return trans; +} + +void +ccnet_db_trans_close (CcnetDBTrans *trans) +{ + db_connection_close (trans->conn); + g_free (trans); +} + +int +ccnet_db_commit (CcnetDBTrans *trans) +{ + DBConnection *conn = trans->conn; + GError *error = NULL; + + if (!db_connection_commit (conn, &error)) { + ccnet_warning ("Commit failed: %s.\n", error->message); + g_clear_error (&error); + return -1; + } + + return 0; +} + +int +ccnet_db_rollback (CcnetDBTrans *trans) +{ + DBConnection *conn = trans->conn; + GError *error = NULL; + + if (!db_connection_commit (conn, &error)) { + ccnet_warning ("Rollback failed: %s.\n", error->message); + g_clear_error (&error); + return -1; + } + + return 0; +} + +static DBStmt * +trans_prepare_statement (DBConnection *conn, const char *sql) +{ + DBStmt *p; + GError *error = NULL; + + p = db_connection_prepare_statement (conn, sql, &error); + if (!p) { + ccnet_warning ("Error prepare statement %s: %s.\n", sql, error->message); + g_clear_error (&error); + return NULL; + } + + return p; +} + +int +ccnet_db_trans_query (CcnetDBTrans *trans, const char *sql, int n, ...) +{ + DBStmt *p; + GError *error = NULL; + + p = trans_prepare_statement (trans->conn, sql); + if (!p) + return -1; + + va_list args; + va_start (args, n); + if (set_parameters_va (p, n, args) < 0) { + va_end (args); + return -1; + } + va_end (args); + + if (!db_stmt_execute (p, &error)) { + ccnet_warning ("Error exec prep stmt: %s.\n", error->message); + g_clear_error (&error); + return -1; + } + + return 0; +} + +gboolean +ccnet_db_trans_check_for_existence (CcnetDBTrans *trans, + const char *sql, + gboolean *db_err, + int n, ...) +{ + ResultSet *result; + gboolean ret = TRUE; + GError *error = NULL; + + *db_err = FALSE; + + DBStmt *p; + + p = trans_prepare_statement (trans->conn, sql); + if (!p) { + *db_err = TRUE; + return FALSE; + } + + va_list args; + va_start (args, n); + if (set_parameters_va (p, n, args) < 0) { + *db_err = TRUE; + va_end (args); + return FALSE; + } + va_end (args); + + result = db_stmt_execute_query (p, &error); + if (error) { + ccnet_warning ("Error exec prep stmt: %s.\n", error->message); + g_clear_error (&error); + *db_err = TRUE; + return FALSE; + } + + if (!result_set_next (result, &error)) + ret = FALSE; + + if (error) { + ccnet_warning ("Error get next result from prep stmt: %s.\n", error->message); + g_clear_error (&error); + *db_err = TRUE; + } + + return ret; +} + +int +ccnet_db_trans_foreach_selected_row (CcnetDBTrans *trans, const char *sql, + CcnetDBRowFunc callback, void *data, + int n, ...) +{ + ResultSet *result; + CcnetDBRow ccnet_row; + int n_rows = 0; + GError *error = NULL; + + DBStmt *p; + + p = trans_prepare_statement (trans->conn, sql); + if (!p) + return FALSE; + + va_list args; + va_start (args, n); + if (set_parameters_va (p, n, args) < 0) { + va_end (args); + return -1; + } + va_end (args); + + result = db_stmt_execute_query (p, &error); + if (error) { + ccnet_warning ("Error exec prep stmt: %s.\n", error->message); + g_clear_error (&error); + return -1; + } + + ccnet_row.res = result; + + while (result_set_next (result, &error)) { + n_rows++; + if (!callback (&ccnet_row, data)) + break; + } + + if (error) { + ccnet_warning ("Error get next result from prep stmt: %s.\n", error->message); + g_clear_error (&error); + n_rows = -1; + } + + return n_rows; +} diff --git a/net/common/ccnet-db.h b/net/common/ccnet-db.h new file mode 100644 index 0000000..025b30e --- /dev/null +++ b/net/common/ccnet-db.h @@ -0,0 +1,133 @@ +#ifndef CCNET_DB_H +#define CCNET_DB_H + +#define DB_SQLITE "sqlite" +#define DB_MYSQL "mysql" +#define DB_PGSQL "pgsql" + +enum { + CCNET_DB_TYPE_SQLITE, + CCNET_DB_TYPE_MYSQL, + CCNET_DB_TYPE_PGSQL, +}; + +typedef struct CcnetDB CcnetDB; +typedef struct CcnetDBRow CcnetDBRow; +typedef struct CcnetDBTrans CcnetDBTrans; + +typedef gboolean (*CcnetDBRowFunc) (CcnetDBRow *, void *); + +CcnetDB * +ccnet_db_new_mysql (const char *host, + int port, + const char *user, + const char *passwd, + const char *db, + const char *unix_socket, + gboolean use_ssl, + const char *charset, + int max_connections); + +CcnetDB * +ccnet_db_new_pgsql (const char *host, + const char *user, + const char *passwd, + const char *db_name, + const char *unix_socket); + +CcnetDB * +ccnet_db_new_sqlite (const char *db_path); + +void +ccnet_db_free (CcnetDB *db); + +int +ccnet_db_type (CcnetDB *db); + +int +ccnet_db_query (CcnetDB *db, const char *sql); + +gboolean +ccnet_db_check_for_existence (CcnetDB *db, const char *sql, gboolean *db_err); + +int +ccnet_db_foreach_selected_row (CcnetDB *db, const char *sql, + CcnetDBRowFunc callback, void *data); + +const char * +ccnet_db_row_get_column_text (CcnetDBRow *row, guint32 idx); + +int +ccnet_db_row_get_column_int (CcnetDBRow *row, guint32 idx); + +gint64 +ccnet_db_row_get_column_int64 (CcnetDBRow *row, guint32 idx); + +int +ccnet_db_get_int (CcnetDB *db, const char *sql); + +gint64 +ccnet_db_get_int64 (CcnetDB *db, const char *sql); + +char * +ccnet_db_get_string (CcnetDB *db, const char *sql); + +/* Transaction related */ + +CcnetDBTrans * +ccnet_db_begin_transaction (CcnetDB *db); + +void +ccnet_db_trans_close (CcnetDBTrans *trans); + +int +ccnet_db_commit (CcnetDBTrans *trans); + +int +ccnet_db_rollback (CcnetDBTrans *trans); + +int +ccnet_db_trans_query (CcnetDBTrans *trans, const char *sql, int n, ...); + +gboolean +ccnet_db_trans_check_for_existence (CcnetDBTrans *trans, + const char *sql, + gboolean *db_err, + int n, ...); + +int +ccnet_db_trans_foreach_selected_row (CcnetDBTrans *trans, const char *sql, + CcnetDBRowFunc callback, void *data, + int n, ...); + +/* Escape a string contant by doubling '\" characters. + */ +char * +ccnet_db_escape_string (CcnetDB *db, const char *from); + +gboolean +pgsql_index_exists (CcnetDB *db, const char *index_name); + +/* Prepared Statements */ + +int +ccnet_db_statement_query (CcnetDB *db, const char *sql, int n, ...); + +gboolean +ccnet_db_statement_exists (CcnetDB *db, const char *sql, int n, ...); + +int +ccnet_db_statement_foreach_row (CcnetDB *db, const char *sql, + CcnetDBRowFunc callback, void *data, + int n, ...); + +int +ccnet_db_statement_get_int (CcnetDB *db, const char *sql, int n, ...); + +gint64 +ccnet_db_statement_get_int64 (CcnetDB *db, const char *sql, int n, ...); + +char * +ccnet_db_statement_get_string (CcnetDB *db, const char *sql, int n, ...); + +#endif diff --git a/net/common/common.h b/net/common/common.h new file mode 100644 index 0000000..abb27f7 --- /dev/null +++ b/net/common/common.h @@ -0,0 +1,31 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef COMMON_H +#define COMMON_H + +#ifdef HAVE_CONFIG_H + #include +#endif + +#include +#include +#include /* uint32_t */ +#include /* size_t */ +#include +#include +#include + +#include "option.h" + +#ifndef ULLONG_MAX + #define ULLONG_MAX 18446744073709551615ULL +#endif + +#define IPEERMGR_APP "IPeerMgr" + +#include + +#include "utils.h" +#include + +#endif diff --git a/net/common/connect-mgr.c b/net/common/connect-mgr.c new file mode 100644 index 0000000..9b162f3 --- /dev/null +++ b/net/common/connect-mgr.c @@ -0,0 +1,693 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include +#include + +#include "net.h" +#include "packet.h" +#include "packet-io.h" +#include "peer.h" +#include "session.h" +#include "handshake.h" +#include "message.h" +#include "peer-mgr.h" +#include "connect-mgr.h" +#include "message-manager.h" +#include "proc-factory.h" + + +#define MAX_RECONNECTIONS_PER_PULSE 5 +#define LISTEN_INTERVAL 1000 /* 1s */ +#define RECONNECT_PERIOD_MSEC 10000 + + +#define DEBUG_FLAG CCNET_DEBUG_CONNECTION +#include "log.h" + +extern void ccnet_peer_set_net_state (CcnetPeer *peer, int net_state); +extern gboolean +ccnet_peer_manager_on_peer_resolved (CcnetPeerManager *manager, + CcnetPeer *peer); +extern void +ccnet_peer_manager_on_peer_resolve_failed (CcnetPeerManager *manager, + CcnetPeer *peer); + +static void dns_lookup_peer (CcnetPeer* peer); + +CcnetConnManager * +ccnet_conn_manager_new (CcnetSession *session) +{ + CcnetConnManager *manager; + + manager = g_new0 (CcnetConnManager, 1); + manager->session = session; + + return manager; +} + +#if 0 +/* Note: The handshake timeout is 10s. + * So intervals[0] must be larger than 10s to + * prevent simultaneously connection tryings. + * Actually any value larger than 10 is ok. + * Here we just set it to 20s. + */ +static int intervals[] = { + 20, + 30, + 60, + 15 * 60, + 30 * 60, + 60 * 60, +}; + +static int +get_reconnect_interval_secs (const CcnetPeer *peer) +{ + if (peer->is_relay) + return intervals[0]; + int index = MIN (peer->num_fails, G_N_ELEMENTS(intervals) - 1); + return intervals[index]; +} +#endif + +void start_keepalive (CcnetPeer *peer) +{ + CcnetProcessor *processor; + CcnetProcFactory *factory = peer->manager->session->proc_factory; + + processor = ccnet_proc_factory_create_master_processor (factory, + "keepalive2", peer); + if (processor == NULL) { + ccnet_warning ("Create keepalive2 processor failed\n"); + return; + } + ccnet_processor_startl (processor, NULL); +} + +static void on_peer_connected (CcnetPeer *peer, CcnetPacketIO *io) +{ + g_return_if_fail (peer->net_state == PEER_DOWN); + + ccnet_peer_set_io (peer, io); + ccnet_peer_set_net_state (peer, PEER_CONNECTED); + start_keepalive (peer); +} + +static void notify_found_peer (CcnetSession *session, CcnetPeer *peer) +{ + char buf[128]; + + sprintf (buf, "found %s\n", peer->id); + CcnetMessage *message = ccnet_message_new (session->base.id, + session->base.id, + "System", buf, 0); + ccnet_message_manager_add_msg (session->msg_mgr, + message, MSG_TYPE_SYS); + +} + +static void on_get_resolve_peer_pub_info_done (CcnetProcessor *processor, + gboolean success, void *data) +{ + if (success) { + gboolean should_continue; + should_continue = ccnet_peer_manager_on_peer_resolved ( + processor->peer->manager, processor->peer); + if (!should_continue) + return; + start_keepalive (processor->peer); + } else { + ccnet_message ("Get resolved peer pub info failed\n"); + ccnet_peer_manager_on_peer_resolve_failed ( + processor->peer->manager, processor->peer); + } +} + +static void on_resolve_peer_connected (CcnetPeer *peer, CcnetPacketIO *io) +{ + CcnetProcessor *processor; + CcnetProcFactory *factory = peer->manager->session->proc_factory; + + ccnet_peer_set_io (peer, io); + ccnet_peer_set_net_state (peer, PEER_CONNECTED); + + processor = ccnet_proc_factory_create_master_processor ( + factory, "get-pubinfo", peer); + if (processor == NULL) { + ccnet_warning ("Create get-pubinfo processor failed\n"); + ccnet_peer_shutdown (peer); + return; + } + g_signal_connect (processor, "done", + G_CALLBACK(on_get_resolve_peer_pub_info_done), NULL); + + ccnet_processor_startl (processor, NULL); +} + +static void on_get_unauth_peer_pub_info_done (CcnetProcessor *processor, + gboolean success, void *data) +{ + if (success) { + notify_found_peer (processor->session, processor->peer); + start_keepalive (processor->peer); + } else + ccnet_peer_shutdown (processor->peer); +} + +static void on_unauthed_peer_connected (CcnetPeer *peer, CcnetPacketIO *io) +{ + CcnetProcessor *processor; + CcnetProcFactory *factory = peer->manager->session->proc_factory; + + ccnet_peer_set_io (peer, io); + ccnet_peer_set_net_state (peer, PEER_CONNECTED); + + processor = ccnet_proc_factory_create_master_processor ( + factory, "get-pubinfo", peer); + if (processor == NULL) { + ccnet_warning ("Create get-pubinfo processor failed\n"); + ccnet_peer_shutdown (peer); + return; + } + g_signal_connect (processor, "done", + G_CALLBACK(on_get_unauth_peer_pub_info_done), NULL); + + ccnet_processor_startl (processor, NULL); +} + +static void set_peer_address_from_socket(CcnetPeer *peer, CcnetPacketIO *io) +{ + if (peer == NULL || io == NULL) + return; + + if (peer->addr_str) + g_free (peer->addr_str); + + struct sockaddr_in addr; + socklen_t len = sizeof(struct sockaddr_in); + int socket = io->socket; + getpeername (socket, (struct sockaddr *)&addr, &len); + char *p = inet_ntoa (addr.sin_addr); + peer->addr_str = strdup(p); +} + +static void +myHandshakeDoneCB (CcnetHandshake *handshake, + CcnetPacketIO *io, + int is_connected, + const char *peer_id, + void *vmanager) +{ + CcnetConnManager *manager = vmanager; + CcnetPeerManager *peerMgr = manager->session->peer_mgr; + CcnetPeer *peer; + + if (!is_connected) { + if (ccnet_packet_io_is_incoming (io)) { + ccnet_warning ("[conn] incoming handshake fails.\n"); + ccnet_packet_io_free (io); + return; + } + + /* temporally use peer, so don't need to increase the reference */ + peer = handshake->peer; + if (peer->num_fails == 0) { + /* print error for the first time */ + ccnet_message ("[Conn] peer %s(%.10s) connection fails\n", + peer->name, peer->id); + } else { + ccnet_debug ("[Conn] peer %s(%.10s) connection fails\n", + peer->name, peer->id); + } + if (peer->net_state == PEER_CONNECTED) { + ccnet_debug ("[Conn] But Peer %s(%.10s) is already connected me.\n", + peer->name, peer->id); + } else if (peer->net_state == PEER_DOWN){ + ccnet_peer_shutdown(peer); + } + + ccnet_packet_io_free (io); + + peer->num_fails++; + peer->in_connection = 0; + return; + } + + if (!ccnet_packet_io_is_incoming (io)) { + peer = handshake->peer; + peer->in_connection = 0; + + if (peer->to_resolve) { + if (!peer_id_valid(peer_id)) { + /* TODO: Remove the peer */ + ccnet_warning ("[Conn] Resolving: Received invalid peer id\n"); + return; + } + ccnet_debug ("[Conn] Resolving: Peer %.8s is resolved\n", peer_id); + memcpy (peer->id, peer_id, 40); + peer->id[40] = '\0'; + on_resolve_peer_connected (peer, io); + return; + } + /* ref for using the peer below */ + g_object_ref (peer); + } else { + /* incoming */ + if (!peer_id_valid (peer_id)) { + ccnet_warning ("Invalid peer (no-id) connecting\n"); + ccnet_packet_io_free (io); + return; + } + peer = ccnet_peer_manager_get_peer (peerMgr, peer_id); + if (!peer) { + ccnet_message ("Unknown peer %s connecting\n", peer_id); + peer = ccnet_peer_new (peer_id); + ccnet_peer_manager_add_peer (peerMgr, peer); + set_peer_address_from_socket(peer, io); + peer->last_up = time(NULL); + on_unauthed_peer_connected (peer, io); + g_object_unref (peer); + return; + } + + if (peer->net_state == PEER_CONNECTED) { + ccnet_message ("[Conn] Peer %s (%.10s) is already connected." + "But a new handshake comes in. Reconnect this peer.\n", + peer->name, peer->id); + ccnet_peer_shutdown_no_delay (peer); + } + + set_peer_address_from_socket(peer, io); + peer->last_up = time(NULL); + } + /* hold a reference on the peer */ + + ccnet_message ("[Conn] Peer %s (%.10s) connected\n", + peer->name, peer->id); + peer->num_fails = 0; + on_peer_connected (peer, io); + g_object_unref (peer); +} + + +/** + * return %TRUE if an outgoing connection is started, %FALSE otherwise. + * + * When peer's ip address is not looked up yet, an dns request will be sent, + * and when dns done, this function will be called again. + */ +gboolean +ccnet_conn_manager_connect_peer (CcnetConnManager *manager, CcnetPeer *peer) +{ + CcnetPacketIO *io; + /* int interval; */ + const char *addr = NULL; + int port = 0; + + if (peer->in_connection) + return FALSE; + + /* time_t now = time(NULL); */ + + if (peer->net_state == PEER_CONNECTED) + return FALSE; + + if (peer->dns_addr) + addr = peer->dns_addr; + else if (!peer->redirected) { + if (!peer->public_addr) + goto err_connect; + + if (is_valid_ipaddr(peer->public_addr)) + addr = peer->public_addr; + else { + dns_lookup_peer (peer); + return TRUE; /* same as out going is started */ + } + } else { + if (!peer->redirect_addr) + goto err_connect; + + if (is_valid_ipaddr(peer->redirect_addr)) + addr = peer->redirect_addr; + else { + dns_lookup_peer (peer); + return TRUE; /* same as out going is started */ + } + } + + if (!addr) + goto err_connect; + + if (!peer->redirected) + port = peer->public_port; + else + port = peer->redirect_port; + + ccnet_peer_update_address (peer, addr, port); + + /* interval = get_reconnect_interval_secs (peer); */ + /* if (now - peer->last_try_time < interval) { */ + /* ccnet_debug ("[Conn] Less than interval: (%d - %d = %d, %d)\n", */ + /* now, peer->last_try_time, now - peer->last_try_time, */ + /* interval); */ + /* return FALSE; */ + /* } */ + /* peer->last_try_time = now; */ + + if (peer->num_fails == 0) { + /* print log for the first time */ + ccnet_message ("[Conn] Start outgoing connect to %s(%.10s) %s:%d\n", + peer->name, peer->id, addr, port); + } else { + ccnet_debug ("[Conn] Start outgoing connect to %s(%.10s) %s:%d\n", + peer->name, peer->id, addr, port); + } + io = ccnet_packet_io_new_outgoing (manager->session, addr, port); + + if (io == NULL) { + /* ccnet_warning ("Failed to create socket for peer %s (%.10s)\n", + peer->name, peer->id); */ + goto err_connect; + } else { + peer->in_connection = 1; + ccnet_handshake_new (manager->session, peer, io, + myHandshakeDoneCB, manager); + return TRUE; + } + +err_connect: + peer->num_fails++; + return FALSE; +} + +static void +reconnect_peer (CcnetConnManager *manager, CcnetPeer *peer) +{ + if (peer->net_state == PEER_CONNECTED || peer->in_connection) + return; +/* + if (peer->public_addr == NULL) { + ccnet_warning ("[conn-mgr] A peer without public_ip and " + "domain is entered in conn_list\n"); + return; + } +*/ + ccnet_conn_manager_connect_peer (manager, peer); +} + +static int reconnect_pulse (void *vmanager) +{ + CcnetConnManager *manager = vmanager; + /* int conn = 0; */ + GList *ptr; + +#ifndef CCNET_SERVER + GList *peers = ccnet_peer_manager_get_peers_with_role ( + manager->session->peer_mgr, "MyRelay"); + for (ptr = peers; ptr; ptr = ptr->next) { + CcnetPeer *peer = ptr->data; + if (peer->redirected) { + if (peer->num_fails > 2) + ccnet_peer_unset_redirect (peer); + } + reconnect_peer (manager, peer); + g_object_unref (peer); + } + g_list_free (peers); +#endif + + for (ptr = manager->conn_list; ptr; ptr = ptr->next) { + CcnetPeer *peer = ptr->data; + if (peer->redirected) { + if (peer->num_fails > 2) + ccnet_peer_unset_redirect (peer); + } + reconnect_peer (manager, peer); + } + + /* + peers = ccnet_peer_manager_get_peers_with_role ( + manager->session->peer_mgr, "MyPeer"); + for (ptr = peers; ptr; ptr = ptr->next) { + CcnetPeer *peer = ptr->data; + + if (peer->is_self) + continue; + + if (!peer->is_relay && !ccnet_peer_is_mypeer(peer)) + continue; + + if (ccnet_conn_manager_connect_peer (manager, peer) == FALSE) + continue; + + if (++conn >= MAX_RECONNECTIONS_PER_PULSE) + break; + } + g_list_free (peers); + */ + + /* TODO: teer down connections */ + + return TRUE; +} + +void +ccnet_conn_manager_add_incoming (CcnetConnManager *manager, + struct sockaddr_storage *cliaddr, + size_t addrlen, + evutil_socket_t socket) +{ + CcnetPacketIO *io; + + io = ccnet_packet_io_new_incoming (manager->session, cliaddr, socket); + ccnet_handshake_new (manager->session, NULL, io, + myHandshakeDoneCB, manager); +} + + +static int +listen_pulse (void * vmanager) +{ + CcnetConnManager *manager = vmanager; + + for ( ;; ) /* check for new incoming peer connections */ + { + evutil_socket_t socket; + struct sockaddr_storage cliaddr; + socklen_t len = sizeof (struct sockaddr_storage); + + if (manager->bind_socket < 0) + break; + + if ((socket = ccnet_net_accept (manager->bind_socket, + &cliaddr, &len, 1)) < 0) + break; + + ccnet_conn_manager_add_incoming (manager, &cliaddr, len, socket); + } + + return TRUE; +} + + +#ifndef CCNET_SERVER +#endif /* ifndef CCNET_SERVER */ + + +void +ccnet_conn_listen_init (CcnetConnManager *manager) +{ + evutil_socket_t socket; + CcnetSession *session = manager->session; + + if (session->base.public_port == 0) { + ccnet_message ("Do not listen for incoming peers\n"); + return; + } + + socket = ccnet_net_bind_tcp (session->base.public_port, 1); + if (socket >= 0) { + ccnet_message ("Opened port %d to listen for " + "incoming peer connections\n", session->base.public_port); + manager->bind_socket = socket; + listen (manager->bind_socket, 5); + } else { + ccnet_error ("Couldn't open port %d to listen for " + "incoming peer connections (errno %d - %s)", + session->base.public_port, errno, strerror(errno) ); + exit (1); + } + + manager->listen_timer = ccnet_timer_new (listen_pulse, manager, + LISTEN_INTERVAL); +} + +typedef struct DNSLookupData { + CcnetPeer *peer; + char *addr_str; +} DNSLookupData; + +static void * +dns_lookup (void *vdata) +{ + DNSLookupData *data = vdata; + struct evutil_addrinfo hints; + struct evutil_addrinfo *answer = NULL; + int err; + void *addr; + char *addr_str; + socklen_t size; + + /* Build the hints to tell getaddrinfo how to act. */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; /* only use IPv4 now. */ + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; /* We want a TCP socket */ + /* Only return addresses we can use. */ + hints.ai_flags = EVUTIL_AI_ADDRCONFIG; + + /* Look up the hostname. */ + err = evutil_getaddrinfo(data->peer->public_addr, NULL, &hints, &answer); + if (err != 0) { + ccnet_warning("Error while resolving '%s': %s\n", + data->peer->public_addr, evutil_gai_strerror(err)); + return vdata; + } + + /* just use the first answer */ + if (answer->ai_family == AF_INET) { + size = INET_ADDRSTRLEN; + addr = &((struct sockaddr_in *)(answer->ai_addr))->sin_addr; + } else if (answer->ai_family == AF_INET6) { + size = INET6_ADDRSTRLEN; + addr = &((struct sockaddr_in6 *)(answer->ai_addr))->sin6_addr; + } else + goto out; + + addr_str = (char *)calloc(size, sizeof(char)); + if (addr_str == NULL) { + ccnet_error("Out of memory\n"); + goto out; + } + + if (!evutil_inet_ntop(answer->ai_family, addr, addr_str, size)) { + ccnet_warning("Peer %s domain name %s lookup fail\n", + data->peer->name, data->peer->public_addr); + free(addr_str); + goto out; + } + + data->addr_str = addr_str; + +out: + evutil_freeaddrinfo (answer); + return vdata; +} + +static void +dns_lookup_cb (void *result) +{ + DNSLookupData *data = result; + CcnetPeer *peer = data->peer; + + if (!data->addr_str) { + ccnet_warning ("DNS lookup failed for peer %.10s(%s).\n", + data->peer->id, data->peer->public_addr); + g_free (data); + return; + } + + g_free(peer->dns_addr); + peer->dns_addr = g_strdup (data->addr_str); + peer->dns_done = 1; + ccnet_conn_manager_connect_peer (peer->manager->session->connMgr, peer); + + free(data->addr_str); + g_free (data); +} + +static void +dns_lookup_peer (CcnetPeer* peer) +{ + DNSLookupData *data; + + if (peer->dns_done) + return; + + data = g_new0 (DNSLookupData, 1); + data->peer = peer; + ccnet_job_manager_schedule_job (peer->manager->session->job_mgr, + dns_lookup, + dns_lookup_cb, + data); +} + +void +ccnet_conn_manager_start (CcnetConnManager *manager) +{ +#ifdef CCNET_SERVER + ccnet_conn_listen_init (manager); +#endif + manager->reconnect_timer = ccnet_timer_new (reconnect_pulse, manager, + RECONNECT_PERIOD_MSEC); +} + +void +ccnet_conn_manager_stop (CcnetConnManager *manager) +{ + evutil_closesocket (manager->bind_socket); + manager->bind_socket = 0; + + ccnet_timer_free (&manager->reconnect_timer); + ccnet_timer_free (&manager->listen_timer); +} + +void +ccnet_conn_manager_add_to_conn_list (CcnetConnManager *manager, + CcnetPeer *peer) +{ + if (g_list_find (manager->conn_list, peer)) { + ccnet_warning ("[Conn] peer %s(%.8s) is already in conn_list\n", + peer->name, peer->id); + return; + } + manager->conn_list = g_list_prepend (manager->conn_list, peer); + g_object_ref (peer); +} + +void +ccnet_conn_manager_remove_from_conn_list (CcnetConnManager *manager, + CcnetPeer *peer) +{ + /* we can't directly use g_list_remove, since we have to call + * g_object_unref, if the peer is actually in the list + */ + if (!g_list_find (manager->conn_list, peer)) + return; + manager->conn_list = g_list_remove (manager->conn_list, peer); + g_object_unref (peer); +} + +void +ccnet_conn_manager_cancel_conn (CcnetConnManager *manager, + const char *addr, int port) +{ + GList *ptr; + + for (ptr = manager->conn_list; ptr; ptr = ptr->next) { + CcnetPeer *peer = ptr->data; + if (g_strcmp0(peer->public_addr, addr) == 0 && peer->public_port == port) { + manager->conn_list = g_list_delete_link (manager->conn_list, ptr); + if (peer->to_resolve) { + ccnet_peer_manager_on_peer_resolve_failed ( + manager->session->peer_mgr, peer); + } + g_object_unref (peer); + break; + } + } +} diff --git a/net/common/connect-mgr.h b/net/common/connect-mgr.h new file mode 100644 index 0000000..c19130c --- /dev/null +++ b/net/common/connect-mgr.h @@ -0,0 +1,40 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_CONNECTION_MANAGER +#define CCNET_CONNECTION_MANAGER + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#else +#include +#endif + +#include "timer.h" + +typedef struct CcnetConnManager CcnetConnManager; + +struct CcnetConnManager +{ + CcnetSession *session; + + CcnetTimer *reconnect_timer; + CcnetTimer *listen_timer; + + evutil_socket_t bind_socket; + + GList *conn_list; +}; + +CcnetConnManager *ccnet_conn_manager_new (CcnetSession *session); +void ccnet_conn_manager_start (CcnetConnManager *manager); +void ccnet_conn_manager_stop (CcnetConnManager *manager); +gboolean ccnet_conn_manager_connect_peer (CcnetConnManager *manager, + CcnetPeer *peer); +void ccnet_conn_manager_add_to_conn_list (CcnetConnManager *manager, + CcnetPeer *peer); +void ccnet_conn_manager_remove_from_conn_list (CcnetConnManager *manager, + CcnetPeer *peer); +void ccnet_conn_manager_cancel_conn (CcnetConnManager *manager, + const char *addr, int port); + +#endif diff --git a/net/common/db-wrapper/Makefile.am b/net/common/db-wrapper/Makefile.am new file mode 100644 index 0000000..0ec7e2e --- /dev/null +++ b/net/common/db-wrapper/Makefile.am @@ -0,0 +1,12 @@ +AM_CFLAGS = -I$(top_srcdir)/net/common -I$(top_srcdir)/lib \ + -I$(top_srcdir)/include -I$(top_srcdir)/include/ccnet \ + -Wall @GLIB2_CFLAGS@ @MSVC_CFLAGS@ @MYSQL_CFLAGS@ @PGSQL_CFLAGS@ + +noinst_LTLIBRARIES = libdbwrapper.la + +noinst_HEADERS = db-wrapper.h mysql-db-ops.h sqlite-db-ops.h pgsql-db-ops.h + +libdbwrapper_la_SOURCES = db-wrapper.c mysql-db-ops.c sqlite-db-ops.c pgsql-db-ops.c + +libdbwrapper_la_LDFLAGS = -Wl,-z -Wl,defs +libdbwrapper_la_LIBADD = @SSL_LIBS@ @GLIB2_LIBS@ @MYSQL_LIBS@ -lsqlite3 @PGSQL_LIBS@ diff --git a/net/common/db-wrapper/db-wrapper.c b/net/common/db-wrapper/db-wrapper.c new file mode 100644 index 0000000..16d7083 --- /dev/null +++ b/net/common/db-wrapper/db-wrapper.c @@ -0,0 +1,359 @@ +#include "common.h" + +#include "db-wrapper.h" +#include "mysql-db-ops.h" +#include "sqlite-db-ops.h" +#include "pgsql-db-ops.h" + +typedef struct DBOperations { + void (*db_conn_pool_free) (DBConnPool *); + DBConnection* (*get_db_connection) (DBConnPool *, GError **); + void (*db_connection_close) (DBConnection *); + gboolean (*db_connection_execute) (DBConnection *, const char *, GError **); + ResultSet* (*db_connection_execute_query) (DBConnection *, const char *, GError **); + gboolean (*result_set_next) (ResultSet *, GError **); + const char* (*result_set_get_string) (ResultSet *, int, GError **); + void (*result_set_free) (ResultSet *); + int (*result_set_get_column_count) (ResultSet *); + DBStmt* (*db_connection_prepare_statement) (DBConnection *, const char *, GError **); + gboolean (*db_stmt_set_int) (DBStmt *, int, int, GError **); + gboolean (*db_stmt_set_int64) (DBStmt *, int, gint64, GError **); + gboolean (*db_stmt_set_string) (DBStmt *, int, const char *, GError **); + gboolean (*db_stmt_execute) (DBStmt *, GError **); + ResultSet* (*db_stmt_execute_query) (DBStmt *, GError **); + void (*db_stmt_free) (DBStmt *); + gboolean (*db_connection_begin_transaction) (DBConnection *, GError **); + gboolean (*db_connection_commit) (DBConnection *, GError **); + gboolean (*db_connection_rollback) (DBConnection *, GError **); +} DBOperations; + +static DBOperations db_ops; + +/* DB Connection Pool. */ + +DBConnPool * +db_conn_pool_new_mysql (const char *host, + const char *user, + const char *password, + unsigned int port, + const char *db_name, + const char *unix_socket, + gboolean use_ssl, + const char *charset, + int max_connections) +{ + db_ops.db_conn_pool_free = mysql_db_conn_pool_free; + db_ops.get_db_connection = mysql_get_db_connection; + db_ops.db_connection_close = mysql_db_connection_close; + db_ops.db_connection_execute = mysql_db_connection_execute; + db_ops.db_connection_execute_query = mysql_execute_query; + db_ops.result_set_next = mysql_result_set_next; + db_ops.result_set_get_string = mysql_result_set_get_string; + db_ops.result_set_free = mysql_result_set_free; + db_ops.result_set_get_column_count = mysql_result_set_get_column_count; + db_ops.db_connection_prepare_statement = mysql_prepare_statement; + db_ops.db_stmt_set_int = mysql_stmt_set_int; + db_ops.db_stmt_set_int64 = mysql_stmt_set_int64; + db_ops.db_stmt_set_string = mysql_stmt_set_string; + db_ops.db_stmt_execute = mysql_db_stmt_execute; + db_ops.db_stmt_execute_query = mysql_db_stmt_execute_query; + db_ops.db_stmt_free = mysql_db_stmt_free; + db_ops.db_connection_begin_transaction = mysql_db_begin_transaction; + db_ops.db_connection_commit = mysql_db_commit; + db_ops.db_connection_rollback = mysql_db_rollback; + + return mysql_db_conn_pool_new (host, user, password, port, db_name, unix_socket, + use_ssl, charset, max_connections); +} + +DBConnPool * +db_conn_pool_new_pgsql (const char *host, + const char *user, + const char *password, + const char *db_name, + const char *unix_socket) +{ + db_ops.db_conn_pool_free = pgsql_db_conn_pool_free; + db_ops.get_db_connection = pgsql_get_db_connection; + db_ops.db_connection_close = pgsql_db_connection_close; + db_ops.db_connection_execute = pgsql_db_connection_execute; + db_ops.db_connection_execute_query = pgsql_execute_query; + db_ops.result_set_next = pgsql_result_set_next; + db_ops.result_set_get_string = pgsql_result_set_get_string; + db_ops.result_set_free = pgsql_result_set_free; + db_ops.result_set_get_column_count = pgsql_result_set_get_column_count; + db_ops.db_connection_prepare_statement = pgsql_prepare_statement; + db_ops.db_stmt_set_int = pgsql_stmt_set_int; + db_ops.db_stmt_set_int64 = pgsql_stmt_set_int64; + db_ops.db_stmt_set_string = pgsql_stmt_set_string; + db_ops.db_stmt_execute = pgsql_db_stmt_execute; + db_ops.db_stmt_execute_query = pgsql_db_stmt_execute_query; + db_ops.db_stmt_free = pgsql_db_stmt_free; + db_ops.db_connection_begin_transaction = pgsql_db_begin_transaction; + db_ops.db_connection_commit = pgsql_db_commit; + db_ops.db_connection_rollback = pgsql_db_rollback; + + return pgsql_db_conn_pool_new (host, user, password, db_name, unix_socket); +} + +DBConnPool * +db_conn_pool_new_sqlite (const char *db_path, int max_connections) +{ + db_ops.db_conn_pool_free = sqlite_db_conn_pool_free; + db_ops.get_db_connection = sqlite_get_db_connection; + db_ops.db_connection_close = sqlite_db_connection_close; + db_ops.db_connection_execute = sqlite_db_connection_execute; + db_ops.db_connection_execute_query = sqlite_execute_query; + db_ops.result_set_next = sqlite_result_set_next; + db_ops.result_set_get_string = sqlite_result_set_get_string; + db_ops.result_set_free = sqlite_result_set_free; + db_ops.result_set_get_column_count = sqlite_result_set_get_column_count; + db_ops.db_connection_prepare_statement = sqlite_prepare_statement; + db_ops.db_stmt_set_int = sqlite_stmt_set_int; + db_ops.db_stmt_set_int64 = sqlite_stmt_set_int64; + db_ops.db_stmt_set_string = sqlite_stmt_set_string; + db_ops.db_stmt_execute = sqlite_db_stmt_execute; + db_ops.db_stmt_execute_query = sqlite_db_stmt_execute_query; + db_ops.db_stmt_free = sqlite_db_stmt_free; + db_ops.db_connection_begin_transaction = sqlite_db_begin_transaction; + db_ops.db_connection_commit = sqlite_db_commit; + db_ops.db_connection_rollback = sqlite_db_rollback; + + return sqlite_db_conn_pool_new (db_path, max_connections); +} + +void +db_conn_pool_free (DBConnPool *pool) +{ + return db_ops.db_conn_pool_free (pool); +} + +/* DB Connections. */ + +DBConnection * +db_conn_pool_get_connection (DBConnPool *pool, GError **error) +{ + return db_ops.get_db_connection (pool, error); +} + +static void +db_connection_clear (DBConnection *conn) +{ + result_set_free (conn->result_set); + + db_stmt_free (conn->stmt); +} + +void +db_connection_close (DBConnection *conn) +{ + if (!conn) + return; + + db_connection_clear (conn); + + db_ops.db_connection_close (conn); +} + +gboolean +db_connection_execute (DBConnection *conn, const char *sql, GError **error) +{ + return db_ops.db_connection_execute (conn, sql, error); +} + +/* Result Sets. */ + +void +result_set_free (ResultSet *r) +{ + if (!r) + return; + + return db_ops.result_set_free (r); +} + +ResultSet * +db_connection_execute_query (DBConnection *conn, const char *sql, GError **error) +{ + ResultSet *result_set; + + if (conn->result_set) { + result_set_free (conn->result_set); + conn->result_set = NULL; + } + + result_set = db_ops.db_connection_execute_query (conn, sql, error); + + if (result_set) + conn->result_set = result_set; + + return result_set; +} + +gboolean +result_set_next (ResultSet *r, GError **error) +{ + return db_ops.result_set_next (r, error); +} + +const char * +result_set_get_string (ResultSet *r, int idx, GError **error) +{ + return db_ops.result_set_get_string (r, idx, error); +} + +int +result_set_get_int (ResultSet *r, int idx, GError **error) +{ + const char *str; + char *e; + int ret; + + str = db_ops.result_set_get_string (r, idx, error); + if (*error) { + return -1; + } + + if (!str) { + return 0; + } + + errno = 0; + ret = strtol (str, &e, 10); + if (errno || (e == str)) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Number conversion failed."); + return -1; + } + + return ret; +} + +gint64 +result_set_get_int64 (ResultSet *r, int idx, GError **error) +{ + const char *str; + char *e; + gint64 ret; + + str = db_ops.result_set_get_string (r, idx, error); + if (*error) { + return -1; + } + + if (!str) { + return 0; + } + + errno = 0; + ret = strtoll (str, &e, 10); + if (errno || (e == str)) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Number conversion failed."); + return -1; + } + + return ret; +} + +int +result_set_get_column_count (ResultSet *r) +{ + return db_ops.result_set_get_column_count (r); +} + +/* Prepared Statements. */ + +DBStmt * +db_connection_prepare_statement (DBConnection *conn, const char *sql, GError **error) +{ + DBStmt *stmt; + + if (conn->stmt) { + db_stmt_free (conn->stmt); + conn->stmt = NULL; + } + + stmt = db_ops.db_connection_prepare_statement (conn, sql, error); + + if (stmt) + conn->stmt = stmt; + + return stmt; +} + +int +db_stmt_set_int (DBStmt *stmt, int idx, int x, GError **error) +{ + return db_ops.db_stmt_set_int (stmt, idx, x, error); +} + +int +db_stmt_set_int64 (DBStmt *stmt, int idx, gint64 x, GError **error) +{ + return db_ops.db_stmt_set_int64 (stmt, idx, x, error); +} + +int +db_stmt_set_string (DBStmt *stmt, int idx, const char *s, GError **error) +{ + return db_ops.db_stmt_set_string (stmt, idx, s, error); +} + +gboolean +db_stmt_execute (DBStmt *stmt, GError **error) +{ + return db_ops.db_stmt_execute (stmt, error); +} + +ResultSet * +db_stmt_execute_query (DBStmt *stmt, GError **error) +{ + ResultSet *result_set; + + if (stmt->result_set) { + result_set_free (stmt->result_set); + stmt->result_set = NULL; + } + + result_set = db_ops.db_stmt_execute_query (stmt, error); + + if (result_set) + stmt->result_set = result_set; + + return result_set; +} + +void +db_stmt_free (DBStmt *stmt) +{ + if (!stmt) + return; + + if (stmt->result_set) + result_set_free (stmt->result_set); + + return db_ops.db_stmt_free (stmt); +} + +/* Transactions. */ + +gboolean +db_connection_begin_transaction (DBConnection *conn, GError **error) +{ + return db_ops.db_connection_begin_transaction (conn, error); +} + +gboolean +db_connection_commit (DBConnection *conn, GError **error) +{ + return db_ops.db_connection_commit (conn, error); +} + +gboolean +db_connection_rollback (DBConnection *conn, GError **error) +{ + db_connection_clear (conn); + + return db_ops.db_connection_rollback (conn, error); +} diff --git a/net/common/db-wrapper/db-wrapper.h b/net/common/db-wrapper/db-wrapper.h new file mode 100644 index 0000000..ad897d6 --- /dev/null +++ b/net/common/db-wrapper/db-wrapper.h @@ -0,0 +1,129 @@ +#ifndef DB_WRAPPER_H +#define DB_WARPPER_H + +#include + +#define SEAF_DB_ERROR_DOMAIN g_quark_from_string("SEAF_DB") +#define SEAF_DB_ERROR_CODE 0 + +/* DB Connection Pool. */ + +struct DBConnPool { + int max_connections; +}; +typedef struct DBConnPool DBConnPool; + +DBConnPool * +db_conn_pool_new_mysql (const char *host, + const char *user, + const char *password, + unsigned int port, + const char *db_name, + const char *unix_socket, + gboolean use_ssl, + const char *charset, + int max_connections); + +DBConnPool * +db_conn_pool_new_pgsql (const char *host, + const char *user, + const char *password, + const char *db_name, + const char *unix_socket); + +DBConnPool * +db_conn_pool_new_sqlite (const char *db_path, int max_connections); + +void +db_conn_pool_free (DBConnPool *pool); + +/* DB Connections. */ + +struct ResultSet; +typedef struct ResultSet ResultSet; + +struct DBStmt; +typedef struct DBStmt DBStmt; + +struct DBConnection { + DBConnPool *pool; + ResultSet *result_set; + DBStmt *stmt; +}; +typedef struct DBConnection DBConnection; + +DBConnection * +db_conn_pool_get_connection (DBConnPool *pool, GError **error); + +void +db_connection_close (DBConnection *conn); + +gboolean +db_connection_execute (DBConnection *conn, const char *sql, GError **error); + +/* Result Sets. */ + +struct ResultSet { + /* Empty */ +}; + +ResultSet * +db_connection_execute_query (DBConnection *conn, const char *sql, GError **error); + +gboolean +result_set_next (ResultSet *r, GError **error); + +const char * +result_set_get_string (ResultSet *r, int idx, GError **error); + +int +result_set_get_int (ResultSet *r, int idx, GError **error); + +gint64 +result_set_get_int64 (ResultSet *r, int idx, GError **error); + +int +result_set_get_column_count (ResultSet *r); + +void +result_set_free (ResultSet *r); + +/* Prepared Statements. */ + +struct DBStmt { + ResultSet *result_set; +}; + +DBStmt * +db_connection_prepare_statement (DBConnection *conn, const char *sql, GError **error); + +gboolean +db_stmt_set_int (DBStmt *stmt, int idx, int x, GError **error); + +gboolean +db_stmt_set_int64 (DBStmt *stmt, int idx, gint64 x, GError **error); + +gboolean +db_stmt_set_string (DBStmt *stmt, int idx, const char *s, GError **error); + +gboolean +db_stmt_execute (DBStmt *stmt, GError **error); + +ResultSet * +db_stmt_execute_query (DBStmt *stmt, GError **error); + +void +db_stmt_free (DBStmt *stmt); + +/* Transactions. */ + +gboolean +db_connection_begin_transaction (DBConnection *conn, GError **error); + +gboolean +db_connection_commit (DBConnection *conn, GError **error); + +gboolean +db_connection_rollback (DBConnection *conn, GError **error); + +#endif diff --git a/net/common/db-wrapper/mysql-db-ops.c b/net/common/db-wrapper/mysql-db-ops.c new file mode 100644 index 0000000..f630f7c --- /dev/null +++ b/net/common/db-wrapper/mysql-db-ops.c @@ -0,0 +1,580 @@ +#include "common.h" + +#include "db-wrapper.h" +#include "mysql-db-ops.h" + +#include + +/* Connection Pool. */ + +typedef struct MySQLDBConnPool { + DBConnPool parent; + char *host; + char *user; + char *password; + unsigned int port; + char *db_name; + char *unix_socket; + gboolean use_ssl; + char *charset; +} MySQLDBConnPool; + +DBConnPool * +mysql_db_conn_pool_new (const char *host, + const char *user, + const char *password, + unsigned int port, + const char *db_name, + const char *unix_socket, + gboolean use_ssl, + const char *charset, + int max_connections) +{ + MySQLDBConnPool *pool = g_new0 (MySQLDBConnPool, 1); + + pool->parent.max_connections = max_connections; + pool->host = g_strdup (host); + pool->user = g_strdup (user); + pool->password = g_strdup (password); + pool->port = port; + pool->db_name = g_strdup(db_name); + pool->unix_socket = g_strdup(unix_socket); + pool->use_ssl = use_ssl; + pool->charset = g_strdup(charset); + + mysql_library_init (0, NULL, NULL); + + return (DBConnPool *)pool; +} + +void +mysql_db_conn_pool_free (DBConnPool *vpool) +{ + MySQLDBConnPool *pool = (MySQLDBConnPool *)vpool; + + g_free (pool->host); + g_free (pool->user); + g_free (pool->password); + g_free (pool->db_name); + g_free (pool->unix_socket); + g_free (pool->charset); + + g_free (pool); +} + +/* Connection. */ + +typedef struct MySQLDBConnection { + DBConnection parent; + MYSQL *db; +} MySQLDBConnection; + +#define SQL_DEFAULT_TCP_TIMEOUT 3 + +static MYSQL * +connect_mysql (MySQLDBConnPool *pool, GError **error) +{ + my_bool yes = 1; + volatile int connect_timeout = SQL_DEFAULT_TCP_TIMEOUT; + unsigned long client_flags = CLIENT_MULTI_STATEMENTS; + MYSQL *db; + + db = mysql_init (NULL); + if (!db) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Failed to allocate mysql handle."); + return NULL; + } + + if (pool->use_ssl) + mysql_ssl_set(db, 0,0,0,0,0); + + if (pool->charset) + mysql_options(db, MYSQL_SET_CHARSET_NAME, pool->charset); + + mysql_options(db, MYSQL_OPT_CONNECT_TIMEOUT, (const char*)&connect_timeout); + mysql_options(db, MYSQL_OPT_RECONNECT, (const char*)&yes); + + if (mysql_real_connect(db, pool->host, pool->user, pool->password, + pool->db_name, pool->port, + pool->unix_socket, client_flags)) { + return db; + } else { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Failed to connect to MySQL: %s", mysql_error(db)); + mysql_close (db); + return NULL; + } +} + +DBConnection * +mysql_get_db_connection (DBConnPool *vpool, GError **error) +{ + MySQLDBConnPool *pool = (MySQLDBConnPool *)vpool; + MySQLDBConnection *conn; + MYSQL *db = connect_mysql (pool, error); + if (!db) + return NULL; + conn = g_new0 (MySQLDBConnection, 1); + conn->db = db; + conn->parent.pool = vpool; + return (DBConnection *)conn; +} + +void +mysql_db_connection_close (DBConnection *vconn) +{ + if (!vconn) + return; + + MySQLDBConnection *conn = (MySQLDBConnection *)vconn; + + mysql_close (conn->db); + + g_free (conn); +} + +gboolean +mysql_db_connection_execute (DBConnection *vconn, const char *sql, GError **error) +{ + MySQLDBConnection *conn = (MySQLDBConnection *)vconn; + + int rc = mysql_real_query (conn->db, sql, strlen(sql)); + if (rc != 0) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "MySQL failed to execute: %s", mysql_error(conn->db)); + return FALSE; + } + return TRUE; +} + +/* Result Set. */ + +#define DEFAULT_COLUMN_SIZE 256 + +typedef struct MySQLResultSet { + ResultSet parent; + MYSQL_STMT *stmt; + int column_count; + MYSQL_BIND *bind; + int need_rebind; +} MySQLResultSet; + +void +mysql_result_set_free (ResultSet *vr) +{ + if (!vr) + return; + + MySQLResultSet *r = (MySQLResultSet *)vr; + + mysql_stmt_free_result (r->stmt); + mysql_stmt_close (r->stmt); + + int i; + for (i = 0; i < r->column_count; ++i) { + g_free (r->bind[i].buffer); + g_free (r->bind[i].length); + g_free (r->bind[i].is_null); + } + g_free (r->bind); + g_free (r); +} + +static MySQLResultSet * +mysql_result_set_new (MYSQL_STMT *stmt, GError **error) +{ + MySQLResultSet *r = g_new0 (MySQLResultSet, 1); + int i; + + r->stmt = stmt; + r->column_count = mysql_stmt_field_count (stmt); + r->bind = g_new0 (MYSQL_BIND, r->column_count); + for (i = 0; i < r->column_count; ++i) { + r->bind[i].buffer = g_malloc (DEFAULT_COLUMN_SIZE + 1); + r->bind[i].buffer_type = MYSQL_TYPE_STRING; + r->bind[i].buffer_length = DEFAULT_COLUMN_SIZE; + r->bind[i].length = g_new0 (unsigned long, 1); + r->bind[i].is_null = g_new0 (my_bool, 1); + } + + if (mysql_stmt_bind_result (stmt, r->bind) != 0) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "mysql_stmt_bind_result failed: %s\n", mysql_stmt_error(stmt)); + mysql_result_set_free ((ResultSet*)r); + return NULL; + } + + return r; +} + +static MYSQL_STMT * +prepare (MYSQL *db, const char *sql, GError **error) +{ + MYSQL_STMT *stmt; + + stmt = mysql_stmt_init (db); + if (!stmt) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "mysql_stmt_init out of memory"); + return NULL; + } + + if (mysql_stmt_prepare (stmt, sql, strlen(sql)) != 0) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "mysql_stmt_prepare failed: %s", + mysql_stmt_error(stmt)); + mysql_stmt_close (stmt); + return NULL; + } + + return stmt; +} + +ResultSet * +mysql_execute_query (DBConnection *vconn, const char *sql, GError **error) +{ + MySQLDBConnection *conn = (MySQLDBConnection *)vconn; + MYSQL_STMT *stmt; + MySQLResultSet *r; + + stmt = prepare (conn->db, sql, error); + if (!stmt) { + return NULL; + } + + unsigned long cursor = CURSOR_TYPE_READ_ONLY; + mysql_stmt_attr_set (stmt, STMT_ATTR_CURSOR_TYPE, &cursor); + + if (mysql_stmt_execute (stmt) != 0) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "mysql_stmt_execute failed: %s", + mysql_stmt_error(stmt)); + mysql_stmt_close (stmt); + return NULL; + } + + r = mysql_result_set_new (stmt, error); + if (!r) { + mysql_stmt_close (stmt); + return NULL; + } + + return (ResultSet *)r; +} + +static gboolean +check_mysql_column_size (MySQLResultSet *r, int i, GError **error) +{ + unsigned long real_length = *(r->bind[i].length); + + if ((real_length > r->bind[i].buffer_length)) { + /* Column was truncated, resize and fetch column directly. */ + g_free (r->bind[i].buffer); + r->bind[i].buffer = g_malloc (real_length + 1); + r->bind[i].buffer_length = real_length; + if (mysql_stmt_fetch_column (r->stmt, &r->bind[i], i, 0) != 0) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "mysql_stmt_fetch_column failed: %s", + mysql_stmt_error(r->stmt)); + return FALSE; + } + r->need_rebind = TRUE; + } + + return TRUE; +} + +gboolean +mysql_result_set_next (ResultSet *vr, GError **error) +{ + MySQLResultSet *r = (MySQLResultSet *)vr; + + if (r->need_rebind) { + if (mysql_stmt_bind_result (r->stmt, r->bind) != 0) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "mysql_stmt_bind_result failed: %s", + mysql_stmt_error(r->stmt)); + return FALSE; + } + r->need_rebind = FALSE; + } + + int rc = mysql_stmt_fetch (r->stmt); + if (rc == 1) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "mysql_stmt_fetch failed: %s", mysql_stmt_error(r->stmt)); + return FALSE; + } + return ((rc == 0) || (rc == MYSQL_DATA_TRUNCATED)); +} + +const char * +mysql_result_set_get_string (ResultSet *vr, int i, GError **error) +{ + MySQLResultSet *r = (MySQLResultSet *)vr; + char *ret; + + if (i < 0 || i >= r->column_count) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Column index is out of range."); + return NULL; + } + + if (*(r->bind[i].is_null)) { + return NULL; + } + + if (!check_mysql_column_size (r, i, error)) { + return NULL; + } + + ret = r->bind[i].buffer; + ret[r->bind[i].buffer_length] = 0; + return ret; +} + +int +mysql_result_set_get_column_count (ResultSet *vr) +{ + MySQLResultSet *r = (MySQLResultSet *)vr; + + return r->column_count; +} + +typedef struct MySQLDBStmt { + DBStmt parent; + int param_count; + MYSQL_STMT *stmt; + MYSQL_BIND *bind; +} MySQLDBStmt; + +static MySQLDBStmt * +mysql_stmt_new (MYSQL_STMT *stmt) +{ + MySQLDBStmt *p = g_new0 (MySQLDBStmt, 1); + + p->stmt = stmt; + p->param_count = (int)mysql_stmt_param_count(stmt); + if (p->param_count>0) { + p->bind = g_new0 (MYSQL_BIND, p->param_count); + } + + return p; +} + +DBStmt * +mysql_prepare_statement (DBConnection *vconn, const char *sql, GError **error) +{ + MySQLDBConnection *conn = (MySQLDBConnection *)vconn; + MYSQL_STMT *stmt; + MySQLDBStmt *ret; + + stmt = prepare (conn->db, sql, error); + if (!stmt) { + return NULL; + } + + ret = mysql_stmt_new (stmt); + + return (DBStmt*)ret; +} + +gboolean +mysql_stmt_set_int (DBStmt *vstmt, int i, int x, GError **error) +{ + MySQLDBStmt *stmt = (MySQLDBStmt *)vstmt; + int *pval; + + if (i < 0 || i >= stmt->param_count) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Column index is out of range."); + return FALSE; + } + + pval = g_new (int, 1); + *pval = x; + + stmt->bind[i].buffer_type = MYSQL_TYPE_LONG; + stmt->bind[i].buffer = (char *)pval; + stmt->bind[i].is_null = 0; + + return TRUE; +} + +gboolean +mysql_stmt_set_int64 (DBStmt *vstmt, int i, gint64 x, GError **error) +{ + MySQLDBStmt *stmt = (MySQLDBStmt *)vstmt; + gint64 *pval; + + if (i < 0 || i >= stmt->param_count) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Column index is out of range."); + return FALSE; + } + + pval = g_new (gint64, 1); + *pval = x; + + stmt->bind[i].buffer_type = MYSQL_TYPE_LONGLONG; + stmt->bind[i].buffer = (char *)pval; + stmt->bind[i].is_null = 0; + + return TRUE; +} + +gboolean +mysql_stmt_set_string (DBStmt *vstmt, int i, const char *s, GError **error) +{ + MySQLDBStmt *stmt = (MySQLDBStmt *)vstmt; + static my_bool yes = TRUE; + unsigned long *plen; + + if (i < 0 || i >= stmt->param_count) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Column index is out of range."); + return FALSE; + } + + stmt->bind[i].buffer_type = MYSQL_TYPE_STRING; + stmt->bind[i].buffer = g_strdup(s); + plen = g_new (unsigned long, 1); + stmt->bind[i].length = plen; + if (!s) { + *plen = 0; + stmt->bind[i].is_null = &yes; + } else { + *plen = strlen(s); + stmt->bind[i].is_null = 0; + } + + return TRUE; +} + +gboolean +mysql_db_stmt_execute (DBStmt *vstmt, GError **error) +{ + MySQLDBStmt *stmt = (MySQLDBStmt *)vstmt; + + if (stmt->param_count > 0 && + mysql_stmt_bind_param (stmt->stmt, stmt->bind) != 0) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "mysql_stmt_bind_param failed: %s", + mysql_stmt_error(stmt->stmt)); + return FALSE; + } + + unsigned long cursor = CURSOR_TYPE_NO_CURSOR; + mysql_stmt_attr_set (stmt->stmt, STMT_ATTR_CURSOR_TYPE, &cursor); + + if (mysql_stmt_execute (stmt->stmt) != 0) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "mysql_stmt_execute failed: %s", mysql_stmt_error(stmt->stmt)); + return FALSE; + } + + mysql_stmt_reset (stmt->stmt); + + return TRUE; +} + +ResultSet * +mysql_db_stmt_execute_query (DBStmt *vstmt, GError **error) +{ + MySQLDBStmt *stmt = (MySQLDBStmt *)vstmt; + + if (stmt->param_count > 0 && + mysql_stmt_bind_param (stmt->stmt, stmt->bind) != 0) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "mysql_stmt_bind_param failed: %s", + mysql_stmt_error(stmt->stmt)); + return NULL; + } + + unsigned long cursor = CURSOR_TYPE_READ_ONLY; + mysql_stmt_attr_set (stmt->stmt, STMT_ATTR_CURSOR_TYPE, &cursor); + + if (mysql_stmt_execute (stmt->stmt) != 0) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "mysql_stmt_execute failed: %s", mysql_stmt_error(stmt->stmt)); + return NULL; + } + + MySQLResultSet *r = mysql_result_set_new (stmt->stmt, error); + if (*error) { + return NULL; + } + + return (ResultSet *)r; +} + +void +mysql_db_stmt_free (DBStmt *vstmt) +{ + if (!vstmt) + return; + + MySQLDBStmt *stmt = (MySQLDBStmt *)vstmt; + + /* If there is a result set associated with this stmt, the mysql stmt + * will be freed when freeing the result set. + */ + if (!stmt->parent.result_set) { + mysql_stmt_free_result (stmt->stmt); + mysql_stmt_close (stmt->stmt); + } + + int i; + for (i = 0; i < stmt->param_count; ++i) { + g_free (stmt->bind[i].buffer); + g_free (stmt->bind[i].length); + } + g_free (stmt->bind); + + g_free (stmt); +} + +/* Transaction. */ + +gboolean +mysql_db_begin_transaction (DBConnection *vconn, GError **error) +{ + MySQLDBConnection *conn = (MySQLDBConnection *)vconn; + + int rc = mysql_query (conn->db, "START TRANSACTION;"); + if (rc != 0) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Failed to begin transaction: %s", mysql_error(conn->db)); + } + + return (rc == 0); +} + +gboolean +mysql_db_commit (DBConnection *vconn, GError **error) +{ + MySQLDBConnection *conn = (MySQLDBConnection *)vconn; + + int rc = mysql_query (conn->db, "COMMIT;"); + if (rc != 0) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Failed to commit transaction: %s", mysql_error(conn->db)); + } + + return (rc == 0); +} + +gboolean +mysql_db_rollback (DBConnection *vconn, GError **error) +{ + MySQLDBConnection *conn = (MySQLDBConnection *)vconn; + + int rc = mysql_query (conn->db, "ROLLBACK;"); + if (rc != 0) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Failed to rollback transaction: %s", mysql_error(conn->db)); + } + + return (rc == 0); +} diff --git a/net/common/db-wrapper/mysql-db-ops.h b/net/common/db-wrapper/mysql-db-ops.h new file mode 100644 index 0000000..4a869d4 --- /dev/null +++ b/net/common/db-wrapper/mysql-db-ops.h @@ -0,0 +1,72 @@ +#ifndef MYSQL_DB_OPS_H +#define MYSQL_DB_OPS_H + +DBConnPool * +mysql_db_conn_pool_new (const char *host, + const char *user, + const char *password, + unsigned int port, + const char *db_name, + const char *unix_socket, + gboolean use_ssl, + const char *charset, + int max_connections); + +void +mysql_db_conn_pool_free (DBConnPool *vpool); + +DBConnection * +mysql_get_db_connection (DBConnPool *vpool, GError **error); + +void +mysql_db_connection_close (DBConnection *vconn); + +gboolean +mysql_db_connection_execute (DBConnection *vconn, const char *sql, GError **error); + +void +mysql_result_set_free (ResultSet *vr); + +ResultSet * +mysql_execute_query (DBConnection *vconn, const char *sql, GError **error); + +gboolean +mysql_result_set_next (ResultSet *vr, GError **error); + +const char * +mysql_result_set_get_string (ResultSet *vr, int i, GError **error); + +int +mysql_result_set_get_column_count (ResultSet *vr); + +DBStmt * +mysql_prepare_statement (DBConnection *vconn, const char *sql, GError **error); + +gboolean +mysql_stmt_set_int (DBStmt *vstmt, int i, int x, GError **error); + +gboolean +mysql_stmt_set_int64 (DBStmt *vstmt, int i, gint64 x, GError **error); + +gboolean +mysql_stmt_set_string (DBStmt *vstmt, int i, const char *s, GError **error); + +gboolean +mysql_db_stmt_execute (DBStmt *vstmt, GError **error); + +ResultSet * +mysql_db_stmt_execute_query (DBStmt *vstmt, GError **error); + +void +mysql_db_stmt_free (DBStmt *vstmt); + +gboolean +mysql_db_begin_transaction (DBConnection *vconn, GError **error); + +gboolean +mysql_db_commit (DBConnection *vconn, GError **error); + +gboolean +mysql_db_rollback (DBConnection *vconn, GError **error); + +#endif diff --git a/net/common/db-wrapper/pgsql-db-ops.c b/net/common/db-wrapper/pgsql-db-ops.c new file mode 100644 index 0000000..3a7fed2 --- /dev/null +++ b/net/common/db-wrapper/pgsql-db-ops.c @@ -0,0 +1,475 @@ +#include "common.h" + +#include "db-wrapper.h" +#include "pgsql-db-ops.h" + +#include + +typedef struct PGDBConnPool { + DBConnPool parent; + char *host; + char *user; + char *password; + char *db_name; + char *unix_socket; +} PGDBConnPool; + +DBConnPool * +pgsql_db_conn_pool_new (const char *host, + const char *user, + const char *password, + const char *db_name, + const char *unix_socket) +{ + PGDBConnPool *pool = g_new0 (PGDBConnPool, 1); + + pool->host = g_strdup (host); + pool->user = g_strdup (user); + pool->password = g_strdup (password); + pool->db_name = g_strdup(db_name); + pool->unix_socket = g_strdup(unix_socket); + + return (DBConnPool *)pool; +} + +void +pgsql_db_conn_pool_free (DBConnPool *vpool) +{ + PGDBConnPool *pool = (PGDBConnPool *)vpool; + + g_free (pool->host); + g_free (pool->user); + g_free (pool->password); + g_free (pool->db_name); + g_free (pool->unix_socket); + + g_free (pool); +} + +typedef struct PGDBConnection { + DBConnection parent; + PGconn *db; +} PGDBConnection; + +static char * +escape_string_pgsql_connect (const char *str) +{ + GString *buf = g_string_new (NULL); + const char *p; + + for (p = str; *p != '\0'; ++p) { + if (*p == '\'' || *p == '\\') { + g_string_append_c (buf, '\\'); + g_string_append_c (buf, *p); + } else { + g_string_append_c (buf, *p); + } + } + + return g_string_free (buf, FALSE); +} + +static PGconn * +connect_pgsql (PGDBConnPool *pool, GError **error) +{ + GString *buf = g_string_new(""); + char *esc_password = NULL; + PGconn *db; + + g_string_append_printf (buf, "user='%s' ", pool->user); + + esc_password = escape_string_pgsql_connect (pool->password); + g_string_append_printf (buf, "password='%s' ", esc_password); + g_free (esc_password); + + if (pool->unix_socket) { + g_string_append_printf (buf, "host='%s' ", pool->unix_socket); + } else { + g_string_append_printf (buf, "host='%s' ", pool->host); + } + + g_string_append_printf (buf, "dbname='%s' ", pool->db_name); + + db = PQconnectdb (buf->str); + if (PQstatus (db) != CONNECTION_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "PQconnectdb failed: %s", PQerrorMessage (db)); + PQfinish (db); + db = NULL; + } + + g_string_free (buf, TRUE); + return db; +} + +DBConnection * +pgsql_get_db_connection (DBConnPool *vpool, GError **error) +{ + PGDBConnPool *pool = (PGDBConnPool *)vpool; + PGDBConnection *conn; + + PGconn *db = connect_pgsql (pool, error); + if (!db) + return NULL; + + conn = g_new0 (PGDBConnection, 1); + conn->db = db; + conn->parent.pool = vpool; + + return (DBConnection *)conn; +} + +void +pgsql_db_connection_close (DBConnection *vconn) +{ + if (!vconn) + return; + + PGDBConnection *conn = (PGDBConnection *)vconn; + + PQfinish (conn->db); + + g_free (conn); +} + +gboolean +pgsql_db_connection_execute (DBConnection *vconn, const char *sql, GError **error) +{ + PGDBConnection *conn = (PGDBConnection *)vconn; + PGresult *res; + gboolean ret = TRUE; + + res = PQexec (conn->db, sql); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "PQexec failed: %s", PQresultErrorMessage(res)); + ret = FALSE; + } + PQclear (res); + + return ret; +} + +typedef struct PGResultSet { + ResultSet parent; + PGresult *res; + int curr_row; + int column_count; + int row_count; +} PGResultSet; + +void +pgsql_result_set_free (ResultSet *vr) +{ + if (!vr) + return; + + PGResultSet *r = (PGResultSet *)vr; + + PQclear (r->res); + g_free (r); +} + +static PGResultSet * +pgsql_result_set_new (PGresult *res) +{ + PGResultSet *r; + + r = g_new0 (PGResultSet, 1); + r->curr_row = -1; + r->column_count = PQnfields(res); + r->row_count = PQntuples(res); + r->res = res; + + return r; +} + +ResultSet * +pgsql_execute_query (DBConnection *vconn, const char *sql, GError **error) +{ + PGDBConnection *conn = (PGDBConnection *)vconn; + PGresult *res; + PGResultSet *r; + + res = PQexec (conn->db, sql); + if (PQresultStatus(res) != PGRES_TUPLES_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "PQexec failed: %s", PQresultErrorMessage(res)); + return NULL; + } + + r = pgsql_result_set_new (res); + + return (ResultSet *)r; +} + +gboolean +pgsql_result_set_next (ResultSet *vr, GError **error) +{ + PGResultSet *r = (PGResultSet *)vr; + + return ((r->curr_row)++ < (r->row_count - 1)); +} + +const char * +pgsql_result_set_get_string (ResultSet *vr, int i, GError **error) +{ + PGResultSet *r = (PGResultSet *)vr; + + if (PQgetisnull(r->res, r->curr_row, i)) + return NULL; + return PQgetvalue(r->res, r->curr_row, i); +} + +int +pgsql_result_set_get_column_count (ResultSet *vr) +{ + PGResultSet *r = (PGResultSet *)vr; + return r->column_count; +} + +typedef struct PGDBStmt { + DBStmt parent; + char *name; + PGconn *db; + int param_count; + char **values; + int *lengths; + int *formats; +} PGDBStmt; + +static PGDBStmt * +pgsql_stmt_new (PGconn *db, char *name, int param_count) +{ + PGDBStmt *stmt = g_new0 (PGDBStmt, 1); + + stmt->name = g_strdup(name); + stmt->db = db; + stmt->param_count = param_count; + + if (stmt->param_count) { + stmt->values = g_new0 (char *, param_count); + stmt->lengths = g_new0 (int, param_count); + stmt->formats = g_new0 (int, param_count); + } + + return stmt; +} + +/* Convert '?' in the query string to $1, $2, etc. */ +static char * +pgsql_format_query_string (const char *sql, int *param_count) +{ + GString *buf = g_string_new (NULL); + const char *p; + int i = 0; + + for (p = sql; *p != '\0'; ++p) { + if (*p == '?') { + ++i; + g_string_append_c (buf, '$'); + g_string_append_printf (buf, "%d", i); + } else { + g_string_append_c (buf, *p); + } + } + + *param_count = i; + + return g_string_free (buf, FALSE); +} + +static gint stmt_id = 0; + +DBStmt * +pgsql_prepare_statement (DBConnection *vconn, const char *sql, GError **error) +{ + PGDBConnection *conn = (PGDBConnection *)vconn; + char *query; + int param_count; + char *name; + PGresult *res; + PGDBStmt *stmt = NULL; + + query = pgsql_format_query_string (sql, ¶m_count); + + g_atomic_int_inc (&stmt_id); + name = g_strdup_printf ("%d", stmt_id); + + res = PQprepare (conn->db, name, query, 0, NULL); + ExecStatusType status = PQresultStatus(res); + if (res && (status == PGRES_EMPTY_QUERY || status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK)) { + stmt = pgsql_stmt_new (conn->db, name, param_count); + } else { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "PQprepare failed: %s", PQresultErrorMessage(res)); + } + + PQclear (res); + g_free (name); + g_free (query); + return (DBStmt *)stmt; +} + +gboolean +pgsql_stmt_set_int (DBStmt *vstmt, int i, int x, GError **error) +{ + PGDBStmt *stmt = (PGDBStmt *)vstmt; + + if (i < 0 || i >= stmt->param_count) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Column index is out of range."); + return FALSE; + } + + stmt->values[i] = g_strdup_printf("%d", x); + stmt->lengths[i] = 0; + stmt->formats[i] = 0; + + return TRUE; +} + +gboolean +pgsql_stmt_set_int64 (DBStmt *vstmt, int i, gint64 x, GError **error) +{ + PGDBStmt *stmt = (PGDBStmt *)vstmt; + + if (i < 0 || i >= stmt->param_count) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Column index is out of range."); + return FALSE; + } + + stmt->values[i] = g_strdup_printf("%"G_GINT64_FORMAT, x); + stmt->lengths[i] = 0; + stmt->formats[i] = 0; + + return TRUE; +} + +gboolean +pgsql_stmt_set_string (DBStmt *vstmt, int i, const char *s, GError **error) +{ + PGDBStmt *stmt = (PGDBStmt *)vstmt; + + if (i < 0 || i >= stmt->param_count) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Column index is out of range."); + return FALSE; + } + + stmt->values[i] = g_strdup(s); + stmt->lengths[i] = 0; + stmt->formats[i] = 0; + + return TRUE; +} + +gboolean +pgsql_db_stmt_execute (DBStmt *vstmt, GError **error) +{ + PGDBStmt *stmt = (PGDBStmt *)vstmt; + PGresult *res; + gboolean ret; + + res = PQexecPrepared (stmt->db, stmt->name, stmt->param_count, + (const char **)stmt->values, stmt->lengths, stmt->formats, 0); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "PGexecPrepared failed: %s", PQresultErrorMessage(res)); + ret = FALSE; + } + + ret = TRUE; + + PQclear(res); + + return ret; +} + +ResultSet * +pgsql_db_stmt_execute_query (DBStmt *vstmt, GError **error) +{ + PGDBStmt *stmt = (PGDBStmt *)vstmt; + PGresult *res; + PGResultSet *ret = NULL; + + res = PQexecPrepared (stmt->db, stmt->name, stmt->param_count, + (const char **)stmt->values, stmt->lengths, stmt->formats, 0); + if (PQresultStatus(res) != PGRES_TUPLES_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "PGexecPrepared failed: %s", PQresultErrorMessage(res)); + } + + ret = pgsql_result_set_new (res); + + return (ResultSet *)ret; +} + +void +pgsql_db_stmt_free (DBStmt *vstmt) +{ + if (!vstmt) + return; + + PGDBStmt *stmt = (PGDBStmt *)vstmt; + + g_free (stmt->name); + + int i; + for (i = 0; i < stmt->param_count; ++i) + g_free (stmt->values[i]); + g_free (stmt->values); + + g_free (stmt->lengths); + g_free (stmt->formats); + g_free (stmt); +} + +gboolean +pgsql_db_begin_transaction (DBConnection *vconn, GError **error) +{ + PGDBConnection *conn = (PGDBConnection *)vconn; + gboolean ret = TRUE; + + PGresult *res = PQexec(conn->db, "BEGIN TRANSACTION;"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Begin transaction failed: %s", PQresultErrorMessage(res)); + ret = FALSE; + } + PQclear(res); + return ret; +} + +gboolean +pgsql_db_commit (DBConnection *vconn, GError **error) +{ + PGDBConnection *conn = (PGDBConnection *)vconn; + gboolean ret = TRUE; + + PGresult *res = PQexec(conn->db, "COMMIT TRANSACTION;"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Commit transaction failed: %s", PQresultErrorMessage(res)); + ret = FALSE; + } + PQclear(res); + return ret; +} + +gboolean +pgsql_db_rollback (DBConnection *vconn, GError **error) +{ + PGDBConnection *conn = (PGDBConnection *)vconn; + gboolean ret = TRUE; + + PGresult *res = PQexec(conn->db, "ROLLBACK TRANSACTION;"); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Rollback transaction failed: %s", PQresultErrorMessage(res)); + ret = FALSE; + } + PQclear(res); + return ret; +} diff --git a/net/common/db-wrapper/pgsql-db-ops.h b/net/common/db-wrapper/pgsql-db-ops.h new file mode 100644 index 0000000..2d87c7d --- /dev/null +++ b/net/common/db-wrapper/pgsql-db-ops.h @@ -0,0 +1,68 @@ +#ifndef PGSQL_DB_OPS_H +#define PGSQL_DB_OPS_H + +DBConnPool * +pgsql_db_conn_pool_new (const char *host, + const char *user, + const char *password, + const char *db_name, + const char *unix_socket); + +void +pgsql_db_conn_pool_free (DBConnPool *vpool); + +DBConnection * +pgsql_get_db_connection (DBConnPool *vpool, GError **error); + +void +pgsql_db_connection_close (DBConnection *vconn); + +gboolean +pgsql_db_connection_execute (DBConnection *vconn, const char *sql, GError **error); + +void +pgsql_result_set_free (ResultSet *vr); + +ResultSet * +pgsql_execute_query (DBConnection *vconn, const char *sql, GError **error); + +gboolean +pgsql_result_set_next (ResultSet *vr, GError **error); + +const char * +pgsql_result_set_get_string (ResultSet *vr, int i, GError **error); + +int +pgsql_result_set_get_column_count (ResultSet *vr); + +DBStmt * +pgsql_prepare_statement (DBConnection *vconn, const char *sql, GError **error); + +gboolean +pgsql_stmt_set_int (DBStmt *vstmt, int i, int x, GError **error); + +gboolean +pgsql_stmt_set_int64 (DBStmt *vstmt, int i, gint64 x, GError **error); + +gboolean +pgsql_stmt_set_string (DBStmt *vstmt, int i, const char *s, GError **error); + +gboolean +pgsql_db_stmt_execute (DBStmt *vstmt, GError **error); + +ResultSet * +pgsql_db_stmt_execute_query (DBStmt *vstmt, GError **error); + +void +pgsql_db_stmt_free (DBStmt *vstmt); + +gboolean +pgsql_db_begin_transaction (DBConnection *vconn, GError **error); + +gboolean +pgsql_db_commit (DBConnection *vconn, GError **error); + +gboolean +pgsql_db_rollback (DBConnection *vconn, GError **error); + +#endif diff --git a/net/common/db-wrapper/sqlite-db-ops.c b/net/common/db-wrapper/sqlite-db-ops.c new file mode 100644 index 0000000..7de7151 --- /dev/null +++ b/net/common/db-wrapper/sqlite-db-ops.c @@ -0,0 +1,454 @@ +#include "common.h" + +#include "db-wrapper.h" +#include "sqlite-db-ops.h" + +#include +#include + +/* SQLite thread synchronization rountines. */ + +typedef struct UnlockNotification { + int fired; + pthread_cond_t cond; + pthread_mutex_t mutex; +} UnlockNotification; + +static void +unlock_notify_cb(void **ap_arg, int n_arg) +{ + int i; + + for (i = 0; i < n_arg; i++) { + UnlockNotification *p = (UnlockNotification *)ap_arg[i]; + pthread_mutex_lock (&p->mutex); + p->fired = 1; + pthread_cond_signal (&p->cond); + pthread_mutex_unlock (&p->mutex); + } +} + +static int +wait_for_unlock_notify(sqlite3 *db) +{ + UnlockNotification un; + un.fired = 0; + pthread_mutex_init (&un.mutex, NULL); + pthread_cond_init (&un.cond, NULL); + + int rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un); + + if (rc == SQLITE_OK) { + pthread_mutex_lock(&un.mutex); + if (!un.fired) + pthread_cond_wait (&un.cond, &un.mutex); + pthread_mutex_unlock(&un.mutex); + } + + pthread_cond_destroy (&un.cond); + pthread_mutex_destroy (&un.mutex); + + return rc; +} + +static int +sqlite3_blocking_step(sqlite3_stmt *stmt) +{ + int rc; + while (SQLITE_LOCKED == (rc = sqlite3_step(stmt))) { + rc = wait_for_unlock_notify(sqlite3_db_handle(stmt)); + if (rc != SQLITE_OK) + break; + sqlite3_reset(stmt); + } + return rc; +} + +static int +sqlite3_blocking_prepare_v2(sqlite3 *db, const char *sql, int sql_len, sqlite3_stmt **pstmt, const char **pz) +{ + int rc; + while (SQLITE_LOCKED == (rc = sqlite3_prepare_v2(db, sql, sql_len, pstmt, pz))) { + rc = wait_for_unlock_notify(db); + if (rc != SQLITE_OK) + break; + } + return rc; +} + +static int +sqlite3_blocking_exec(sqlite3 *db, const char *sql, int (*callback)(void *, int, char **, char **), void *arg, char **errmsg) +{ + int rc; + while (SQLITE_LOCKED == (rc = sqlite3_exec(db, sql, callback, arg, errmsg))) { + rc = wait_for_unlock_notify(db); + if (rc != SQLITE_OK) + break; + } + return rc; +} + +typedef struct SQLiteDBConnPool { + DBConnPool parent; + char *db_path; + int max_connections; +} SQLiteDBConnPool; + +DBConnPool * +sqlite_db_conn_pool_new (const char *db_path, int max_connections) +{ + SQLiteDBConnPool *pool = g_new0 (SQLiteDBConnPool, 1); + pool->db_path = g_strdup(db_path); + pool->max_connections = max_connections; + + return (DBConnPool *)pool; +} + +void +sqlite_db_conn_pool_free (DBConnPool *vpool) +{ + if (!vpool) + return; + + SQLiteDBConnPool *pool = (SQLiteDBConnPool *)vpool; + + g_free (pool->db_path); + g_free (pool); +} + +typedef struct SQLiteDBConnection { + DBConnection parent; + sqlite3 *db; +} SQLiteDBConnection; + +DBConnection * +sqlite_get_db_connection (DBConnPool *vpool, GError **error) +{ + SQLiteDBConnPool *pool = (SQLiteDBConnPool *)vpool; + sqlite3 *db; + int result; + const char *errmsg; + SQLiteDBConnection *conn; + + result = sqlite3_open (pool->db_path, &db); + if (result != SQLITE_OK) { + errmsg = sqlite3_errmsg(db); + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Failed to open sqlite db: %s", + errmsg ? errmsg : "no error given"); + return NULL; + } + + conn = g_new0 (SQLiteDBConnection, 1); + conn->db = db; + + return (DBConnection *)conn; +} + +void +sqlite_db_connection_close (DBConnection *vconn) +{ + if (!vconn) + return; + + SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn; + + sqlite3_close (conn->db); + + g_free (conn); +} + +gboolean +sqlite_db_connection_execute (DBConnection *vconn, const char *sql, GError **error) +{ + SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn; + char *errmsg = NULL; + int rc; + + rc = sqlite3_blocking_exec (conn->db, sql, NULL, NULL, &errmsg); + if (rc != SQLITE_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "sqlite3_exec failed: %s", + errmsg ? errmsg : "no error given"); + if (errmsg) + sqlite3_free (errmsg); + return FALSE; + } + + return TRUE; +} + +typedef struct SQLiteResultSet { + ResultSet parent; + sqlite3 *db; + sqlite3_stmt *stmt; + int column_count; +} SQLiteResultSet; + +void +sqlite_result_set_free (ResultSet *vr) +{ + if (!vr) + return; + + SQLiteResultSet *r = (SQLiteResultSet *)vr; + + sqlite3_finalize (r->stmt); + + g_free (r); +} + +ResultSet * +sqlite_execute_query (DBConnection *vconn, const char *sql, GError **error) +{ + SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn; + sqlite3_stmt *stmt; + int rc; + SQLiteResultSet *r; + + rc = sqlite3_blocking_prepare_v2 (conn->db, sql, -1, &stmt, NULL); + if (rc != SQLITE_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "sqlite3_prepare_v2 failed: %s", sqlite3_errmsg(conn->db)); + return NULL; + } + + r = g_new0 (SQLiteResultSet, 1); + r->db = conn->db; + r->stmt = stmt; + r->column_count = sqlite3_column_count (stmt); + + return (ResultSet *)r; +} + +gboolean +sqlite_result_set_next (ResultSet *vr, GError **error) +{ + SQLiteResultSet *r = (SQLiteResultSet *)vr; + int rc; + + rc = sqlite3_blocking_step (r->stmt); + if (rc != SQLITE_ROW && rc != SQLITE_DONE) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "sqlite3_step failed: %s", sqlite3_errmsg(r->db)); + return FALSE; + } + + return (rc == SQLITE_ROW); +} + +const char * +sqlite_result_set_get_string (ResultSet *vr, int i, GError **error) +{ + SQLiteResultSet *r = (SQLiteResultSet *)vr; + + if (i < 0 || i >= r->column_count) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Column index is out of range."); + return NULL; + } + + return (const char *)sqlite3_column_text (r->stmt, i); +} + +int +sqlite_result_set_get_column_count (ResultSet *vr) +{ + return ((SQLiteResultSet *)vr)->column_count; +} + +typedef struct SQLiteDBStmt { + DBStmt parent; + int param_count; + sqlite3 *db; + sqlite3_stmt *stmt; +} SQLiteDBStmt; + +DBStmt * +sqlite_prepare_statement (DBConnection *vconn, const char *sql, GError **error) +{ + SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn; + sqlite3_stmt *stmt; + int rc; + SQLiteDBStmt *ret; + + rc = sqlite3_blocking_prepare_v2 (conn->db, sql, -1, &stmt, NULL); + if (rc != SQLITE_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "sqlite3_prepare_v2 failed: %s", sqlite3_errmsg(conn->db)); + return NULL; + } + + ret = g_new0 (SQLiteDBStmt, 1); + ret->stmt = stmt; + ret->db = conn->db; + ret->param_count = sqlite3_bind_parameter_count (stmt); + + return (DBStmt *)ret; +} + +gboolean +sqlite_stmt_set_int (DBStmt *vstmt, int i, int x, GError **error) +{ + SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt; + int rc; + + if (i < 0 || i >= stmt->param_count) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Column index is out of range."); + return FALSE; + } + + rc = sqlite3_bind_int (stmt->stmt, i+1, x); + if (rc != SQLITE_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "sqlite3_bind_int failed: %s", sqlite3_errstr(rc)); + return FALSE; + } + + return TRUE; +} + +gboolean +sqlite_stmt_set_int64 (DBStmt *vstmt, int i, gint64 x, GError **error) +{ + SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt; + int rc; + + if (i < 0 || i >= stmt->param_count) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Column index is out of range."); + return FALSE; + } + + rc = sqlite3_bind_int64 (stmt->stmt, i+1, x); + if (rc != SQLITE_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "sqlite3_bind_int failed: %s", sqlite3_errstr(rc)); + return FALSE; + } + + return TRUE; +} + +gboolean +sqlite_stmt_set_string (DBStmt *vstmt, int i, const char *s, GError **error) +{ + SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt; + int rc; + + if (i < 0 || i >= stmt->param_count) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Column index is out of range."); + return FALSE; + } + + rc = sqlite3_bind_text (stmt->stmt, i+1, s, -1, SQLITE_TRANSIENT); + if (rc != SQLITE_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "sqlite3_bind_int failed: %s", sqlite3_errstr(rc)); + return FALSE; + } + + return TRUE; +} + +gboolean +sqlite_db_stmt_execute (DBStmt *vstmt, GError **error) +{ + SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt; + int rc; + + rc = sqlite3_blocking_step (stmt->stmt); + if (rc == SQLITE_DONE) { + sqlite3_reset (stmt->stmt); + return TRUE; + } else if (rc == SQLITE_ROW) { + sqlite3_reset (stmt->stmt); + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "Select statement not allowed in db_stmt_execute."); + return FALSE; + } else { + sqlite3_reset (stmt->stmt); + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "sqlite3_step failed: %s", sqlite3_errmsg(stmt->db)); + return FALSE; + } +} + +ResultSet * +sqlite_db_stmt_execute_query (DBStmt *vstmt, GError **error) +{ + SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt; + SQLiteResultSet *r; + + r = g_new0 (SQLiteResultSet, 1); + r->db = stmt->db; + r->stmt = stmt->stmt; + r->column_count = sqlite3_column_count (r->stmt); + + return (ResultSet *)r; +} + +void +sqlite_db_stmt_free (DBStmt *vstmt) +{ + if (!vstmt) + return; + + SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt; + + if (!stmt->parent.result_set) { + sqlite3_finalize (stmt->stmt); + } + + g_free (stmt); +} + +gboolean +sqlite_db_begin_transaction (DBConnection *vconn, GError **error) +{ + SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn; + int rc; + + rc = sqlite3_blocking_exec (conn->db, "BEGIN TRANSACTION;", NULL, NULL, NULL); + if (rc != SQLITE_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "begin transaction failed: %s", sqlite3_errmsg(conn->db)); + return FALSE; + } + + return TRUE; +} + +gboolean +sqlite_db_commit (DBConnection *vconn, GError **error) +{ + SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn; + int rc; + + rc = sqlite3_blocking_exec (conn->db, "COMMIT TRANSACTION;", NULL, NULL, NULL); + if (rc != SQLITE_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "commit transaction failed: %s", sqlite3_errmsg(conn->db)); + return FALSE; + } + + return TRUE; +} + +gboolean +sqlite_db_rollback (DBConnection *vconn, GError **error) +{ + SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn; + int rc; + + rc = sqlite3_blocking_exec (conn->db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL); + if (rc != SQLITE_OK) { + g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE, + "rollback transaction failed: %s", sqlite3_errmsg(conn->db)); + return FALSE; + } + + return TRUE; +} diff --git a/net/common/db-wrapper/sqlite-db-ops.h b/net/common/db-wrapper/sqlite-db-ops.h new file mode 100644 index 0000000..13f9835 --- /dev/null +++ b/net/common/db-wrapper/sqlite-db-ops.h @@ -0,0 +1,64 @@ +#ifndef SQLITE_DB_OPS_H +#define SQLITE_DB_OPS_H + +DBConnPool * +sqlite_db_conn_pool_new (const char *db_path, int max_connections); + +void +sqlite_db_conn_pool_free (DBConnPool *vpool); + +DBConnection * +sqlite_get_db_connection (DBConnPool *vpool, GError **error); + +void +sqlite_db_connection_close (DBConnection *vconn); + +gboolean +sqlite_db_connection_execute (DBConnection *vconn, const char *sql, GError **error); + +void +sqlite_result_set_free (ResultSet *vr); + +ResultSet * +sqlite_execute_query (DBConnection *vconn, const char *sql, GError **error); + +gboolean +sqlite_result_set_next (ResultSet *vr, GError **error); + +const char * +sqlite_result_set_get_string (ResultSet *vr, int i, GError **error); + +int +sqlite_result_set_get_column_count (ResultSet *vr); + +DBStmt * +sqlite_prepare_statement (DBConnection *vconn, const char *sql, GError **error); + +gboolean +sqlite_stmt_set_int (DBStmt *vstmt, int i, int x, GError **error); + +gboolean +sqlite_stmt_set_int64 (DBStmt *vstmt, int i, gint64 x, GError **error); + +gboolean +sqlite_stmt_set_string (DBStmt *vstmt, int i, const char *s, GError **error); + +gboolean +sqlite_db_stmt_execute (DBStmt *vstmt, GError **error); + +ResultSet * +sqlite_db_stmt_execute_query (DBStmt *vstmt, GError **error); + +void +sqlite_db_stmt_free (DBStmt *vstmt); + +gboolean +sqlite_db_begin_transaction (DBConnection *vconn, GError **error); + +gboolean +sqlite_db_commit (DBConnection *vconn, GError **error); + +gboolean +sqlite_db_rollback (DBConnection *vconn, GError **error); + +#endif diff --git a/net/common/getgateway.c b/net/common/getgateway.c new file mode 100644 index 0000000..0c6f49f --- /dev/null +++ b/net/common/getgateway.c @@ -0,0 +1,315 @@ +/* $Id: getgateway.c,v 1.12 2008/10/06 10:04:16 nanard Exp $ */ +/* libnatpmp + * Copyright (c) 2007-2008, Thomas BERNARD + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include +#include +/* There is no portable method to get the default route gateway. + * So below are three differents functions implementing this. + * Parsing /proc/net/route is for linux. + * sysctl is the way to access such informations on BSD systems. + * Many systems should provide route information through raw PF_ROUTE + * sockets. */ +#ifdef WIN32 +#define USE_WIN32 +#undef USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#undef USE_PROC_NET_ROUTE +#endif +#ifdef __linux__ +#define USE_PROC_NET_ROUTE +#undef USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#endif + +#ifdef BSD +#undef USE_PROC_NET_ROUTE +#define USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#endif + +#ifdef __APPLE__ +#undef USE_PROC_NET_ROUTE +#undef USE_SOCKET_ROUTE +#define USE_SYSCTL_NET_ROUTE +#endif + +#if (defined(sun) && defined(__SVR4)) +#undef USE_PROC_NET_ROUTE +#define USE_SOCKET_ROUTE +#undef USE_SYSCTL_NET_ROUTE +#endif + +#ifdef USE_SYSCTL_NET_ROUTE +#include +#include +#include +#include +#endif +#ifdef USE_SOCKET_ROUTE +#include +#include +#include +#include +#include +#endif +#include "getgateway.h" + +#ifndef WIN32 +#define SUCCESS (0) +#define FAILED (-1) +#endif + +#ifdef WIN32 + #include + #include + #include + #include + #include "net.h" +#else + #include + #include + #include +#endif + + +#include + +#include "log.h" + +/* + * get default gateway and then judge it + * reverse ip: + * 10.0.0.0/8 + * 172.16.0.0/12 + * 192.168.0.0/16 + */ +int in_nat(void) +{ +#ifdef WIN32 + //return 0; +#endif + struct in_addr addr; + int one, two; + char *ip; + + if (getdefaultgateway(&addr.s_addr) == -1) { + fprintf(stderr, "Could not get default gateway\n"); + return -1; + } + ip = inet_ntoa(addr); + ccnet_message ("ip = %s\n", ip); + sscanf(ip, "%d.%d", &one, &two); + + if (10 == one) + return 1; + else if (172 == one && 16 == two) + return 1; + else if (192 == one && 168 == two) + return 1; + else + return 0; +} + +#ifdef USE_PROC_NET_ROUTE +int getdefaultgateway(in_addr_t * addr) +{ + long d, g; + char buf[256]; + int line = 0; + FILE * f; + char * p; + f = fopen("/proc/net/route", "r"); + if(!f) + return FAILED; + while(fgets(buf, sizeof(buf), f)) { + if(line > 0) { + p = buf; + while(*p && !isspace(*p)) + p++; + while(*p && isspace(*p)) + p++; + if(sscanf(p, "%lx%lx", &d, &g)==2) { + if(d == 0) { /* default */ + *addr = g; + fclose(f); + return SUCCESS; + } + } + } + line++; + } + /* default route not found ! */ + if(f) + fclose(f); + return FAILED; +} +#endif /* #ifdef USE_PROC_NET_ROUTE */ + + +#ifdef USE_SYSCTL_NET_ROUTE + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + +int getdefaultgateway(in_addr_t * addr) +{ +#if 0 + /* net.route.0.inet.dump.0.0 ? */ + int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, + NET_RT_DUMP, 0, 0/*tableid*/}; +#endif + /* net.route.0.inet.flags.gateway */ + int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, + NET_RT_FLAGS, RTF_GATEWAY}; + size_t l; + char * buf, * p; + struct rt_msghdr * rt; + struct sockaddr * sa; + struct sockaddr * sa_tab[RTAX_MAX]; + int i; + int r = FAILED; + if(sysctl(mib, sizeof(mib)/sizeof(int), 0, &l, 0, 0) < 0) { + return FAILED; + } + if(l>0) { + buf = malloc(l); + if(sysctl(mib, sizeof(mib)/sizeof(int), buf, &l, 0, 0) < 0) { + free(buf); + return FAILED; + } + for(p=buf; prtm_msglen) { + rt = (struct rt_msghdr *)p; + sa = (struct sockaddr *)(rt + 1); + for(i=0; irtm_addrs & (1 << i)) { + sa_tab[i] = sa; + sa = (struct sockaddr *)((char *)sa + ROUNDUP(sa->sa_len)); + } else { + sa_tab[i] = NULL; + } + } + if( ((rt->rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY)) + && sa_tab[RTAX_DST]->sa_family == AF_INET + && sa_tab[RTAX_GATEWAY]->sa_family == AF_INET) { + if(((struct sockaddr_in *)sa_tab[RTAX_DST])->sin_addr.s_addr == 0) { + *addr = ((struct sockaddr_in *)(sa_tab[RTAX_GATEWAY]))->sin_addr.s_addr; + r = SUCCESS; + } + } + } + free(buf); + } + return r; +} +#endif /* #ifdef USE_SYSCTL_NET_ROUTE */ + + +#ifdef USE_SOCKET_ROUTE +/* Thanks to Darren Kenny for this code */ +#define NEXTADDR(w, u) \ + if (rtm_addrs & (w)) {\ + l = sizeof(struct sockaddr); memmove(cp, &(u), l); cp += l;\ + } + +#define rtm m_rtmsg.m_rtm + +struct { + struct rt_msghdr m_rtm; + char m_space[512]; +} m_rtmsg; + +int getdefaultgateway(in_addr_t *addr) +{ + int s, seq, l, rtm_addrs, i; + pid_t pid; + struct sockaddr so_dst, so_mask; + char *cp = m_rtmsg.m_space; + struct sockaddr *gate = NULL, *sa; + struct rt_msghdr *msg_hdr; + + pid = getpid(); + seq = 0; + rtm_addrs = RTA_DST | RTA_NETMASK; + + memset(&so_dst, 0, sizeof(so_dst)); + memset(&so_mask, 0, sizeof(so_mask)); + memset(&rtm, 0, sizeof(struct rt_msghdr)); + + rtm.rtm_type = RTM_GET; + rtm.rtm_flags = RTF_UP | RTF_GATEWAY; + rtm.rtm_version = RTM_VERSION; + rtm.rtm_seq = ++seq; + rtm.rtm_addrs = rtm_addrs; + + so_dst.sa_family = AF_INET; + so_mask.sa_family = AF_INET; + + NEXTADDR(RTA_DST, so_dst); + NEXTADDR(RTA_NETMASK, so_mask); + + rtm.rtm_msglen = l = cp - (char *)&m_rtmsg; + + s = socket(PF_ROUTE, SOCK_RAW, 0); + + if (write(s, (char *)&m_rtmsg, l) < 0) { + close(s); + return FAILED; + } + + do { + l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg)); + } while (l > 0 && (rtm.rtm_seq != seq || rtm.rtm_pid != pid)); + + close(s); + + msg_hdr = &rtm; + + cp = ((char *)(msg_hdr + 1)); + if (msg_hdr->rtm_addrs) { + for (i = 1; i; i <<= 1) + if (i & msg_hdr->rtm_addrs) { + sa = (struct sockaddr *)cp; + if (i == RTA_GATEWAY ) + gate = sa; + + cp += sizeof(struct sockaddr); + } + } else { + return FAILED; + } + + + if (gate != NULL ) { + *addr = ((struct sockaddr_in *)gate)->sin_addr.s_addr; + return SUCCESS; + } else { + return FAILED; + } +} +#endif /* #ifdef USE_SOCKET_ROUTE */ + +#ifdef USE_WIN32 +int getdefaultgateway(in_addr_t *addr) +{ + MIB_IPFORWARDROW ip_forward; + memset(&ip_forward, 0, sizeof(ip_forward)); + if(GetBestRoute(inet_addr("0.0.0.0"), 0, &ip_forward) != NO_ERROR) + return -1; + *addr = ip_forward.dwForwardNextHop; + return 0; +} + +#endif diff --git a/net/common/getgateway.h b/net/common/getgateway.h new file mode 100644 index 0000000..49fe647 --- /dev/null +++ b/net/common/getgateway.h @@ -0,0 +1,40 @@ +/* $Id: getgateway.h,v 1.3 2008/07/02 22:33:06 nanard Exp $ */ +/* libnatpmp + * Copyright (c) 2007, Thomas BERNARD + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifndef __GETGATEWAY_H__ +#define __GETGATEWAY_H__ + +#include +#ifdef WIN32 + typedef unsigned long in_addr_t; +#else + #include +#endif + +/* in_nat() : + * return value : + * 1 : in a nat network + * 0 : not + * -1: not connected to a network + */ +int in_nat(void); + +/* getdefaultgateway() : + * return value : + * 0 : success + * -1 : failure */ +int getdefaultgateway(in_addr_t * addr); + +#endif diff --git a/net/common/handshake.c b/net/common/handshake.c new file mode 100644 index 0000000..c0d6e63 --- /dev/null +++ b/net/common/handshake.c @@ -0,0 +1,239 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#ifdef WIN32 + #include +#else + #include +#endif + +#include "peer.h" +#include "packet-io.h" +#include "session.h" +#include "handshake.h" + +#define DEBUG_FLAG CCNET_DEBUG_CONNECTION +#include "log.h" + + +#define HANDSHAKE_TIMEOUT 10 + +/* + The purpose of handshake is to obtain the peer's Peer ID. + + Handshake Protocol: + + Master Slave + + Master's Peer ID INIT + -----------------------------> + ID_SENT + Slave's Peer ID + <---------------------------- + ID_RECEIVED + ACK + ----------------------------> + DONE DONE + + */ + +enum { + UNKNOWN = 0, + INIT, + ID_SENT, + ID_RECEIVED +}; + +static void ccnet_handshake_done (CcnetHandshake *handshake, int isOK); + +static void +send_handshake_message (CcnetHandshake *handshake) +{ + const char *id = handshake->session->base.id; + char buf[256]; + ccnet_packet *packet = (ccnet_packet *)buf; + + packet->header.version = 1; + packet->header.type = CCNET_MSG_HANDSHAKE; + memcpy (packet->data, id, 40); + packet->header.length = 40; + packet->header.id = 0; + + ccnet_packet_io_write_packet (handshake->io, packet); + + if (handshake->peer) + ccnet_debug ("[Conn] Outgoing: Send my id to %s(%.10s)\n", + handshake->peer->name, handshake->peer->id); + else + ccnet_debug ("[Conn] Incoming: Send my id to %.10s\n", + handshake->id); +} + +static void +send_ack (CcnetHandshake *handshake) +{ + ccnet_packet packet; + packet.header.version = 1; + packet.header.type = CCNET_MSG_OK; + packet.header.length = 0; + packet.header.id = 0; + + ccnet_packet_io_write_packet (handshake->io, &packet); + + ccnet_debug ("[Conn] Outgoing: Send ack to %s(%.10s)\n", + handshake->peer->name, handshake->peer->id); +} + +static void +read_peer_id (CcnetHandshake *handshake, ccnet_packet *packet) +{ + uint16_t len; + char *id; + + /* get id */ + len = packet->header.length; + id = g_malloc (len + 1); + memcpy (id, packet->data, len); + id[len] = '\0'; + handshake->id = id; + + if (handshake->state == INIT) { + /* we are the slave */ + ccnet_debug ("[Conn] Incoming: Read peer id %.8s\n", id); + send_handshake_message (handshake); + handshake->state = ID_RECEIVED; + } else if (handshake->state == ID_SENT) { + /* we are the master */ + ccnet_debug ("[Conn] Outgoing: Read peer %s id %.8s\n", + handshake->peer->name, id); + if (g_strcmp0 (handshake->peer->id, handshake->id) != 0) { + ccnet_warning ("[Conn] Received peer id does not match.\n"); + ccnet_handshake_done (handshake, FALSE); + return; + } + + send_ack (handshake); + ccnet_handshake_done (handshake, TRUE); + } +} + + +static void +read_ok (CcnetHandshake *handshake, ccnet_packet *packet) +{ + if (packet->header.type != CCNET_MSG_OK) { + ccnet_warning ("[Conn] Read wrong ack format\n"); + ccnet_handshake_done (handshake, FALSE); + } else { + ccnet_debug ("[Conn] Incoming: Read ack (%.10s)\n", handshake->id); + ccnet_handshake_done (handshake, TRUE); + } + + return; +} + + +static void +canRead (ccnet_packet *packet, void *arg) +{ + CcnetHandshake *handshake = (CcnetHandshake *)arg; + ccnet_debug("current state is %d\n", handshake->state); + + switch (handshake->state) { + case INIT: + read_peer_id (handshake, packet); + break; + case ID_SENT: + read_peer_id (handshake, packet); + break; + case ID_RECEIVED: + read_ok (handshake, packet); + break; + default: + g_return_if_reached (); + } +} + +static void +fire_done_func (CcnetHandshake *handshake, int is_connected) +{ + const char * peer_id; + + peer_id = is_connected ? handshake->id : NULL; + + (*handshake->doneCB) (handshake, + handshake->io, + is_connected, + peer_id, + handshake->doneUserData); +} + +static void +ccnet_handshake_done (CcnetHandshake *handshake, int isOK) +{ + /* ccnet_message ("handshakeDone: %s\n", isOK ? "connected" : "aborting"); */ + ccnet_debug ("[Conn] HandshakeDone %s\n", isOK ? "connected" : "aborting"); + + ccnet_packet_io_set_iofuncs (handshake->io, NULL, NULL, NULL, NULL); + fire_done_func (handshake, isOK); + if (handshake->peer) + g_object_unref (handshake->peer); + g_free (handshake->id); + g_free (handshake); +} + +void +ccnet_handshake_abort (CcnetHandshake * handshake) +{ + ccnet_handshake_done (handshake, FALSE); +} + +#include + +static void +gotError (struct bufferevent * evbuf, short what, void *arg) +{ + CcnetHandshake * handshake = (CcnetHandshake *) arg; + + /* if the error happened while we were sending a public key, we might + * have encountered a peer that doesn't do encryption... reconnect and + * try a plaintext handshake */ + /* ccnet_warning ("libevent got an error on peer %s what==%d, errno=%d (%s)\n", + handshake->peer ? handshake->peer->name : "(no-id)", + (int)what, errno, strerror(errno)); */ + ccnet_handshake_abort (handshake); +} + + +CcnetHandshake* +ccnet_handshake_new (CcnetSession *session, + CcnetPeer *peer, + CcnetPacketIO *io, + handshakeDoneCB doneCB, + void *doneUserData) +{ + CcnetHandshake * handshake; + + handshake = g_new0 (CcnetHandshake, 1); + handshake->peer = peer; + if (peer) + g_object_ref (peer); + handshake->io = io; + handshake->doneCB = doneCB; + handshake->doneUserData = doneUserData; + handshake->session = session; + + ccnet_packet_io_set_timeout_secs (io, HANDSHAKE_TIMEOUT); + ccnet_packet_io_set_iofuncs (handshake->io, canRead, NULL, + gotError, handshake); + + if (ccnet_packet_io_is_incoming (handshake->io)) + handshake->state = INIT; + else { + send_handshake_message (handshake); + handshake->state = ID_SENT; + } + + return handshake; +} diff --git a/net/common/handshake.h b/net/common/handshake.h new file mode 100644 index 0000000..6d404c6 --- /dev/null +++ b/net/common/handshake.h @@ -0,0 +1,38 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_HANDSHAKE_H +#define CCNET_HANDSHAKE_H + +struct CcnetPacketIO; +typedef struct CcnetHandshake CcnetHandshake; + +#include "peer.h" + + +typedef void (*handshakeDoneCB) (CcnetHandshake *handshake, + CcnetPacketIO *io, + int isConnected, + const char *peerId, + void *userData); + +struct CcnetHandshake +{ + char *id; /* the peer id */ + CcnetPeer *peer; /* only valid if it is outgoing */ + CcnetPacketIO *io; + struct CcnetSession *session; + + uint8_t state; + + handshakeDoneCB doneCB; + void *doneUserData; +}; + +CcnetHandshake* ccnet_handshake_new (CcnetSession *session, + CcnetPeer *peer, + CcnetPacketIO *io, + handshakeDoneCB doneCB, + void *doneUserData); + + +#endif diff --git a/net/common/log.c b/net/common/log.c new file mode 100644 index 0000000..1fb7522 --- /dev/null +++ b/net/common/log.c @@ -0,0 +1,169 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include +#include +#include + +#include "log.h" +#include "utils.h" + +#include "session.h" + +#include "message.h" +#include "message-manager.h" + +extern CcnetSession *session; + +/* message with greater log levels will be ignored */ +static int ccnet_log_level; +static char *logfile; +static FILE *logfp; + +static void +ccnet_log (const gchar *log_domain, GLogLevelFlags log_level, + const gchar *message, gpointer user_data) +{ + time_t t; + struct tm *tm; + char buf[256]; + /* CcnetMessage *ccnet_message; */ + int len; + + if (log_level > ccnet_log_level) + return; + + t = time(NULL); + tm = localtime(&t); + len = strftime (buf, 256, "[%x %X] ", tm); + fputs (buf, logfp); + fputs (message, logfp); + fflush (logfp); + + /* Note, log module starts earlier than session, only + * send syslog when session exists */ + if (log_level <= G_LOG_LEVEL_MESSAGE && session) { + int n = strlen (message); + int max = 256 - len - 1; + if (n > max) n = max; + memcpy (buf + len, message, n); + buf[len + n] = '\0'; + } + + /* + if (log_level < G_LOG_LEVEL_MESSAGE) { + g_on_error_stack_trace (NULL); + } + */ +} + +static int +get_debug_level(const char *str, int default_level) +{ + if (strcmp(str, "debug") == 0) + return G_LOG_LEVEL_DEBUG; + if (strcmp(str, "info") == 0) + return G_LOG_LEVEL_INFO; + if (strcmp(str, "warning") == 0) + return G_LOG_LEVEL_WARNING; + return default_level; +} + +int +ccnet_log_init (const char *_logfile, const char *debug_level_str) +{ + g_log_set_handler (NULL, G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL + | G_LOG_FLAG_RECURSION, ccnet_log, NULL); + + /* write all warnings from lib/ into ccnet.log */ + g_log_set_handler ("Ccnet", G_LOG_LEVEL_WARNING, ccnet_log, NULL); + + /* record all log message */ + ccnet_log_level = get_debug_level(debug_level_str, G_LOG_LEVEL_INFO); + + if (strcmp(_logfile, "-") == 0) { + logfp = stdout; + logfile = g_strdup (_logfile); + } + else { + logfile = ccnet_expand_path (_logfile); + + if ((logfp = g_fopen (logfile, "a+")) == NULL) { + ccnet_message ("Failed to open file %s\n", logfile); + return -1; + } + } + + return 0; +} + +int +ccnet_log_reopen () +{ + FILE *fp, *oldfp; + + if (strcmp(logfile, "-") == 0) + return 0; + + if ((fp = g_fopen (logfile, "a+")) == NULL) { + ccnet_message ("Failed to open file %s\n", logfile); + return -1; + } + + //TODO: check file's health + + oldfp = logfp; + logfp = fp; + if (fclose(oldfp) < 0) { + ccnet_message ("Failed to close file %s\n", logfile); + return -1; + } + + return 0; +} + +static CcnetDebugFlags debug_flags = 0; + +static GDebugKey debug_keys[] = { + { "Peer", CCNET_DEBUG_PEER }, + { "Processor", CCNET_DEBUG_PROCESSOR }, + { "Netio", CCNET_DEBUG_NETIO }, + { "Message", CCNET_DEBUG_MESSAGE }, + { "Connection", CCNET_DEBUG_CONNECTION }, + { "Other", CCNET_DEBUG_OTHER }, +}; + +gboolean +ccnet_debug_flag_is_set (CcnetDebugFlags flag) +{ + return (debug_flags & flag) != 0; +} + +void +ccnet_debug_set_flags (CcnetDebugFlags flags) +{ + ccnet_message ("Set debug flags %#x\n", flags); + debug_flags |= flags; +} + +void +ccnet_debug_set_flags_string (const gchar *flags_string) +{ + guint nkeys = G_N_ELEMENTS (debug_keys); + + if (flags_string) + ccnet_debug_set_flags ( + g_parse_debug_string (flags_string, debug_keys, nkeys)); +} + +void +ccnet_debug_impl (CcnetDebugFlags flag, const gchar *format, ...) +{ + if (flag & debug_flags) { + va_list args; + va_start (args, format); + g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, format, args); + va_end (args); + } +} diff --git a/net/common/log.h b/net/common/log.h new file mode 100644 index 0000000..cdaeffa --- /dev/null +++ b/net/common/log.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_LOG_H +#define CCNET_LOG_H + +#ifndef ccnet_warning +#define ccnet_warning(fmt, ...) g_warning("%s(%d): " fmt, __FILE__, __LINE__, ##__VA_ARGS__) +#endif + +#ifndef ccnet_error +#define ccnet_error(fmt, ...) g_error("%s(%d): " fmt, __FILE__, __LINE__, ##__VA_ARGS__) +#endif + +#ifndef ccnet_message +#define ccnet_message(fmt, ...) g_message("%s(%d): " fmt, __FILE__, __LINE__, ##__VA_ARGS__) +#endif + +int ccnet_log_init (const char *logfile, const char *log_level_str); +int ccnet_log_reopen (); + +typedef enum +{ + CCNET_DEBUG_PEER = 1 << 1, + CCNET_DEBUG_PROCESSOR = 1 << 2, + CCNET_DEBUG_NETIO = 1 << 3, + CCNET_DEBUG_CONNECTION = 1 << 4, + CCNET_DEBUG_MESSAGE = 1 << 5, + CCNET_DEBUG_OTHER = 1 << 6, +} CcnetDebugFlags; + +gboolean ccnet_debug_flag_is_set (CcnetDebugFlags flag); +void ccnet_debug_set_flags (CcnetDebugFlags flag); +void ccnet_debug_set_flags_string (const gchar *flags_string); + +void ccnet_debug_impl (CcnetDebugFlags flag, const gchar *format, ...); + +#define ccnet_debug(format, ...) + + +#endif /* CCNET_LOG_H */ + +#undef ccnet_debug +#define ccnet_debug(format, ...) + +#ifdef DEBUG_FLAG + +#undef ccnet_debug +#define ccnet_debug(fmt, ...) \ + ccnet_debug_impl (DEBUG_FLAG, "%.15s(%d): " fmt, __FILE__, __LINE__, ##__VA_ARGS__) + +#endif /* DEBUG_FLAG */ + + diff --git a/net/common/message-manager.c b/net/common/message-manager.c new file mode 100644 index 0000000..8523d1b --- /dev/null +++ b/net/common/message-manager.c @@ -0,0 +1,175 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "ccnet-db.h" +#include "timer.h" + +#include "peer.h" +#include "session.h" +#include "processors/mqserver-proc.h" + +#include "message.h" +#include "message-manager.h" +#include "peer-mgr.h" + +#define DEBUG_FLAG CCNET_DEBUG_MESSAGE +#include "log.h" + + +struct MessageManagerPriv { + GHashTable *subscribers; + +#ifdef CCNET_SERVER + +#endif +}; + +#define GET_PRIV(o) \ +(G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_MESSAGE_MANAGER, MessageManagerPriv)) + +G_DEFINE_TYPE (CcnetMessageManager, ccnet_message_manager, G_TYPE_OBJECT); + + +static void +ccnet_message_manager_class_init (CcnetMessageManagerClass *class) +{ + /* GObjectClass *object_class; */ + + g_type_class_add_private (class, sizeof (MessageManagerPriv)); +} + +static void +ccnet_message_manager_init (CcnetMessageManager *manager) +{ + manager->priv = GET_PRIV(manager); +} + +CcnetMessageManager * +ccnet_message_manager_new (CcnetSession *session) +{ + CcnetMessageManager *manager; + + manager = g_object_new (CCNET_TYPE_MESSAGE_MANAGER, NULL); + manager->session = session; + + manager->priv->subscribers = g_hash_table_new_full ( + g_str_hash, g_str_equal, g_free, NULL); + + return manager; +} + +int +ccnet_message_manager_start (CcnetMessageManager *manager) +{ + return 0; +} + +static gboolean +handle_inner_message (CcnetMessageManager *manager, + CcnetMessage *msg) +{ + if (strcmp(msg->app, IPEERMGR_APP) == 0) { + ccnet_peer_manager_receive_message (manager->session->peer_mgr, msg); + return TRUE; + } + + return FALSE; +} + +int +ccnet_message_manager_add_msg(CcnetMessageManager *manager, + CcnetMessage *msg, + int msg_type) +{ + MessageManagerPriv *priv = manager->priv; + GList *app_subscribers, *ptr; + CcnetProcessor *processor; + + switch (msg_type) { + case MSG_TYPE_RECV: + if (handle_inner_message(manager, msg)) + break; + + app_subscribers = g_hash_table_lookup (priv->subscribers, + msg->app); + if (!app_subscribers) + break; + + ptr = app_subscribers; + while (ptr) { + processor = ptr->data; + ccnet_mqserver_proc_put_message (processor, msg); + ptr = ptr->next; + } + break; + case MSG_TYPE_SYS: + app_subscribers = g_hash_table_lookup (priv->subscribers, msg->app); + if (!app_subscribers) + break; + + ptr = app_subscribers; + while (ptr) { + processor = ptr->data; + ccnet_mqserver_proc_put_message (processor, msg); + ptr = ptr->next; + } + break; + } + + return 0; +} + +int +ccnet_message_manager_subscribe_app (CcnetMessageManager *manager, + CcnetProcessor *mq_proc, + int n_app, char **apps) +{ + MessageManagerPriv *priv = manager->priv; + GList *app_subscribers; + int i; + + for (i = 0; i < n_app; ++i) { + ccnet_debug ("[Msg] subscribe app %s\n", apps[i]); + + app_subscribers = g_hash_table_lookup (priv->subscribers, apps[i]); + app_subscribers = g_list_prepend (app_subscribers, mq_proc); + g_hash_table_replace (priv->subscribers, g_strdup (apps[i]), + app_subscribers); + } + + return 0; +} + +int +ccnet_message_manager_unsubscribe_app (CcnetMessageManager *manager, + CcnetProcessor *mq_proc, + int n_app, char **apps) +{ + MessageManagerPriv *priv = manager->priv; + GList *app_subscribers; + int i; + int ret = 0; + + for (i = 0; i < n_app; ++i) { + app_subscribers = g_hash_table_lookup (priv->subscribers, apps[i]); + if (!app_subscribers) { + ccnet_warning ("cannot unsubscribe from app %s, " + "no such app subscribed.\n", apps[i]); + ret = -1; + continue; + } + + ccnet_debug ("[Msg] unsubscribe app %s\n", apps[i]); + + app_subscribers = g_list_remove (app_subscribers, mq_proc); + + if (app_subscribers) + g_hash_table_replace (priv->subscribers, g_strdup (apps[i]), + app_subscribers); + else + g_hash_table_remove (priv->subscribers, apps[i]); + } + + return ret; +} diff --git a/net/common/message-manager.h b/net/common/message-manager.h new file mode 100644 index 0000000..d9838c1 --- /dev/null +++ b/net/common/message-manager.h @@ -0,0 +1,52 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_MESSAGE_MANAGER_H +#define CCNET_MESSAGE_MANAGER_H + +#include + +#define CCNET_TYPE_MESSAGE_MANAGER (ccnet_message_manager_get_type ()) +#define CCNET_MESSAGE_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CCNET_TYPE_MESSAGE_MANAGER, CcnetMessageManager)) +#define CCNET_MESSAGE_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), CCNET_TYPE_MESSAGE_MANAGER, CcnetMessageManagerClass)) +#define CCNET_IS_MESSAGE_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CCNET_TYPE_MESSAGE_MANAGER)) +#define CCNET_IS_MESSAGE_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CCNET_TYPE_MESSAGE_MANAGER)) +#define CCNET_MESSAGE_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CCNET_TYPE_MESSAGE_MANAGER, CcnetMessageManagerClass)) + +typedef struct _CcnetMessageManager CcnetMessageManager; +typedef struct _CcnetMessageManagerClass CcnetMessageManagerClass; + +typedef struct MessageManagerPriv MessageManagerPriv; + +struct _CcnetMessageManager { + GObject parent_instance; + + CcnetSession *session; + + MessageManagerPriv *priv; +}; + +struct _CcnetMessageManagerClass { + GObjectClass parent_class; +}; + + +GType ccnet_message_manager_get_type (void); + +CcnetMessageManager* ccnet_message_manager_new (CcnetSession *session); + +int ccnet_message_manager_start (CcnetMessageManager *manager); + +int ccnet_message_manager_add_msg(CcnetMessageManager *manager, + CcnetMessage *msg, + int msg_type); + +int ccnet_message_manager_subscribe_app (CcnetMessageManager *manager, + CcnetProcessor *mq_proc, + int n_app, char **apps); + +int ccnet_message_manager_unsubscribe_app (CcnetMessageManager *manager, + CcnetProcessor *mq_proc, + int n_app, char **apps); + + +#endif diff --git a/net/common/message.c b/net/common/message.c new file mode 100644 index 0000000..12f76b0 --- /dev/null +++ b/net/common/message.c @@ -0,0 +1,281 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include + +#include "message.h" +#include "ccnet-db.h" +#include "string-util.h" + +static CcnetMessage * +ccnet_message_new_full (const char *from, + const char *to, + const char *app, + const char *body, + time_t ctime, + time_t rtime, + const char *id, + char flags) +{ + CcnetMessage *message = g_new0 (CcnetMessage, 1); + + g_return_val_if_fail (flags >= 0, NULL); + g_return_val_if_fail (from != NULL && to != NULL && app != NULL, NULL); + + message->flags = flags; + memcpy (message->from, from, 40); + message->from[40] = '\0'; + memcpy (message->to, to, 40); /* ok if strlen(to) == 36 */ + message->to[40] = '\0'; + message->app = g_intern_string(app); + message->body = g_strdup(body); + message->ctime = (ctime ? ctime : time(NULL)); + message->rtime = rtime; + message->id = (id ? g_strdup (id) : gen_uuid()); + + message->ref_count = 1; + + return message; +} + +CcnetMessage * +ccnet_message_new (const char *from, + const char *to, + const char *app, + const char *body, + char flags) +{ + return ccnet_message_new_full (from, to, app, body, 0, 0, NULL, flags); +} + +void +ccnet_message_ref (CcnetMessage *message) +{ + message->ref_count++; +} + +void +ccnet_message_unref (CcnetMessage *message) +{ + if (--message->ref_count == 0) + ccnet_message_free (message); +} + +void +ccnet_message_free (CcnetMessage *message) +{ + g_free (message->id); + g_free (message->body); + g_free (message); +} + +void +ccnet_message_to_string_buf_local (CcnetMessage *msg, GString *buf) +{ + g_string_printf (buf, "%d %s %s %s %d %d %s %s", msg->flags, + msg->from, + msg->to, + msg->id, + (int)msg->ctime, + (int)msg->rtime, + msg->app, + msg->body); +} + +void +ccnet_message_to_string_buf (CcnetMessage *msg, GString *buf) +{ + g_string_printf (buf, "%d %s %s %s %d %s %s", msg->flags, + msg->from, + msg->to, + msg->id, + (int)msg->ctime, + msg->app, + msg->body); +} + + +CcnetMessage * +ccnet_message_from_string (char *buf, int len) +{ + char flags; + char *from_id, *to_id, *msg_id, *body, *p, *time, *app; + int ctime, rcv_time = 0; + CcnetMessage *message; + + g_return_val_if_fail (END_0(buf,len), NULL); + + p = buf; + while (*p != ' ' && *p) ++p; + if (*p != ' ') + goto error; + *p = '\0'; + flags = atoi (buf); + + from_id = ++p; + p += 40; + if (*p != ' ') + goto error; + *p = '\0'; + + to_id = ++p; + p += 40; /* SHA-1 */ + if (*p != ' ') + goto error; + *p = '\0'; + + msg_id = ++p; + p += 36; + if (*p != ' ') + goto error; + *p = '\0'; + + time = ++p; + while (*p != ' ' && *p) ++p; + if (*p != ' ') + goto error; + *p = '\0'; + ctime = atoi (time); + + app = ++p; + while (*p != ' ' && *p) ++p; + if (*p != ' ') + goto error; + *p = '\0'; + body = p + 1; + + message = ccnet_message_new_full (from_id, to_id, + app, body, + ctime, rcv_time, + msg_id, flags); + return message; + +error: + return NULL; +} + +CcnetMessage * +ccnet_message_from_string_local (char *buf, int len) +{ + int flags; + char *from_id, *to_id, *msg_id, *body, *p, *time, *app; + int ctime, rcv_time = 0; + CcnetMessage *message; + + g_return_val_if_fail (END_0(buf,len), NULL); + + p = buf; + while (*p != ' ' && *p) ++p; + if (*p != ' ') + goto error; + *p = '\0'; + flags = atoi (buf); + + from_id = ++p; + p += 40; + if (*p != ' ') + goto error; + *p = '\0'; + + to_id = ++p; + p += 40; /* SHA-1 */ + if (*p != ' ') + goto error; + *p = '\0'; + + msg_id = ++p; + p += 36; + if (*p != ' ') + goto error; + *p = '\0'; + + time = ++p; + while (*p != ' ' && *p) ++p; + if (*p != ' ') + goto error; + *p = '\0'; + ctime = atoi (time); + + time = ++p; + while (*p != ' ' && *p) ++p; + if (*p != ' ') + goto error; + *p = '\0'; + rcv_time = atoi (time); + + app = ++p; + while (*p != ' ' && *p) ++p; + if (*p != ' ') + goto error; + *p = '\0'; + body = p + 1; + + message = ccnet_message_new_full (from_id, to_id, + app, body, + ctime, rcv_time, + msg_id, flags); + return message; + +error: + return NULL; +} + +#if 0 +CcnetMessage * +ccnet_message_from_db_stmt (CcnetDBRow *stmt) +{ + const char *msg_id, *from, *to, *app, *body, *bloom; + int ctime, rtime; + char flags; + CcnetMessage *message; + + flags = ccnet_db_row_get_column_int (stmt, MSG_DB_COLUMN_FLAGS); + msg_id = (char *)ccnet_db_row_get_column_text (stmt, MSG_DB_COLUMN_MSG_ID); + from = (char *)ccnet_db_row_get_column_text (stmt, MSG_DB_COLUMN_FROM_ID); + to = (char *)ccnet_db_row_get_column_text (stmt, MSG_DB_COLUMN_TO_ID); + bloom = (char *)ccnet_db_row_get_column_text (stmt, MSG_DB_COLUMN_BLOOM); + + ctime = ccnet_db_row_get_column_int (stmt, MSG_DB_COLUMN_CTIME); + rtime = ccnet_db_row_get_column_int (stmt, MSG_DB_COLUMN_RTIME); + app = (char *)ccnet_db_row_get_column_text (stmt, MSG_DB_COLUMN_APP); + body = (char *)ccnet_db_row_get_column_text (stmt, MSG_DB_COLUMN_BODY); + + app = g_intern_string(app); + + message = ccnet_message_new_full (from, to, + app, body, + ctime, rtime, + msg_id, flags); + + message->db_id = ccnet_db_row_get_column_int64 (stmt, MSG_DB_COLUMN_DB_ID); + if (bloom) + message->bloom = g_strdup(bloom); + else + message->bloom = 0; + + return message; +} + + +void +ccnet_message_to_db (CcnetDB *db, const char *table, CcnetMessage *message) +{ + char *sql; + sql = ccnet_sql_printf ("INSERT INTO %s VALUES (" + "NULL, %u, '%s', '%s', '%s', %Q, %u, %u, %Q, %Q);", + table, + message->flags, + message->id, + message->from, + message->to, + message->bloom, /* may be NULL */ + message->ctime, + message->rtime, + message->app, + message->body); + + ccnet_db_query (db, sql); + ccnet_sql_free (sql); +} +#endif /* 0 */ diff --git a/net/common/message.h b/net/common/message.h new file mode 100644 index 0000000..73748bd --- /dev/null +++ b/net/common/message.h @@ -0,0 +1,70 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_MESSAGE_H +#define CCNET_MESSAGE_H + +#include "common.h" +#include "ccnet-db.h" + +enum { + MSG_TYPE_RECV, + MSG_TYPE_SYS, +}; + +enum { + MSG_DB_COLUMN_DB_ID = 0, + MSG_DB_COLUMN_FLAGS, /* "%u" */ + MSG_DB_COLUMN_MSG_ID, /* "%s" */ + MSG_DB_COLUMN_FROM_ID, /* "%s" */ + MSG_DB_COLUMN_TO_ID, /* "%s" */ + MSG_DB_COLUMN_BLOOM, /* "%s" */ + MSG_DB_COLUMN_CTIME, /* %u */ + MSG_DB_COLUMN_RTIME, /* %u */ + MSG_DB_COLUMN_APP, /* %s */ + MSG_DB_COLUMN_BODY, /* %s */ + NUM_MSG_DB_COLUMNS +}; + +#define MESSAGE_ID_LEN 36 + +typedef struct _CcnetMessage CcnetMessage; + +#define FLAG_IS_ERROR 0x02 +#define FLAG_IS_ACK 0x08 +#define FLAG_IS_RENDEZVOUS 0x10 + +struct _CcnetMessage { + int ref_count; + + gint64 db_id; + char flags; + char *id; /* UUID */ + char from[41]; + char to[41]; + int ctime; /* creation time */ + int rtime; /* receive time */ + + const char *app; /* application */ + char *body; +}; + + +CcnetMessage* ccnet_message_new (const char *from, + const char *to, + const char *app, + const char *body, + char flags); + +void ccnet_message_free (CcnetMessage *msg); + +void ccnet_message_ref (CcnetMessage *); +void ccnet_message_unref (CcnetMessage *); + +void ccnet_message_to_string_buf (CcnetMessage *msg, GString *buf); +void ccnet_message_to_string_buf_local (CcnetMessage *msg, GString *buf); + +CcnetMessage *ccnet_message_from_string (char *buf, int len); +CcnetMessage *ccnet_message_from_string_local (char *buf, int len); + + +#endif diff --git a/net/common/packet-io.c b/net/common/packet-io.c new file mode 100644 index 0000000..e134a40 --- /dev/null +++ b/net/common/packet-io.c @@ -0,0 +1,274 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#ifdef WIN32 + #include +#else + #include + #include +#endif + +#include "net.h" + +#include + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#include +#include +#else +#include +#endif + +#include +#include +#include + + +#include "session.h" +#include "packet-io.h" + +#include "log.h" + +#define IO_TIMEOUT_SECS 8 + +/* The watermark of the underlying evbuffer. When there are more data than + * this value is remained in evbuffer, the read event will be removed. + * So, it must be greater than the max length of a single ccnet packet. + */ +#define CCNET_RDBUF 100000 + +static void +didWriteWrapper (struct bufferevent *e, void *user_data) +{ + CcnetPacketIO * c = (CcnetPacketIO *) user_data; + if (c->didWrite) + c->didWrite (e, c->user_data); +} + +static void +canReadWrapper (struct bufferevent *e, void *user_data) +{ + CcnetPacketIO *c = user_data; + ccnet_packet *packet; + uint32_t len; + + g_return_if_fail (sizeof(ccnet_header) == CCNET_PACKET_LENGTH_HEADER); + + c->handling = 1; + + /* We have set up the low watermark. The following must be true. */ + g_return_if_fail (EVBUFFER_LENGTH (e->input) >= CCNET_PACKET_LENGTH_HEADER); + + if (c->canRead == NULL) { + c->handling = 0; + return; + } + + while (1) { + packet = (ccnet_packet *) EVBUFFER_DATA (e->input); + + if (packet->header.type == CCNET_MSG_ENCPACKET) + len = ntohl (packet->header.id); + else + len = ntohs (packet->header.length); + + if (EVBUFFER_LENGTH (e->input) - CCNET_PACKET_LENGTH_HEADER < len) + break; /* wait for more data */ + + /* byte order, from network to host */ + packet->header.length = len; + packet->header.id = ntohl (packet->header.id); + c->canRead (packet, c->user_data); + + /* PacketIO may be scheduled to free in the previous call */ + if (c->schedule_free) { + c->schedule_free = 0; + c->handling = 0; + ccnet_packet_io_free (c); + return; + } + + evbuffer_drain (e->input, len + CCNET_PACKET_LENGTH_HEADER); + + if(EVBUFFER_LENGTH(e->input) >= CCNET_PACKET_LENGTH_HEADER) + continue; + + break; + } + + c->handling = 0; +} + +static void +gotErrorWrapper (struct bufferevent *e, short what, void *user_data) +{ + CcnetPacketIO *c = user_data; + if (c->gotError) + c->gotError (e, what, c->user_data); +} + + +void bufferevent_setwatermark(struct bufferevent *, short, size_t, size_t); + +static CcnetPacketIO* +ccnet_packet_io_new (struct CcnetSession *session, + const struct sockaddr_storage *addr, + int is_incoming, + evutil_socket_t socket) +{ + CcnetPacketIO *io; + + io = g_new0 (CcnetPacketIO, 1); + + io->session = session; + io->socket = socket; + io->is_incoming = is_incoming; + if (addr) { + io->addr = g_malloc(sizeof(struct sockaddr_storage)); + memcpy (io->addr, addr, sizeof(struct sockaddr_storage)); + } + + io->bufev = bufferevent_socket_new (NULL, io->socket, BEV_OPT_CLOSE_ON_FREE); + bufferevent_setcb (io->bufev, canReadWrapper, + didWriteWrapper, gotErrorWrapper, io); + bufferevent_enable (io->bufev, EV_READ | EV_WRITE); + bufferevent_setwatermark (io->bufev, EV_READ, CCNET_PACKET_LENGTH_HEADER, + CCNET_RDBUF); + + /* do not BEV_OPT_CLOSE_ON_FREE, since ccnet_packet_io_free() will + * handle it */ + /* io->bufev = bufferevent_socket_new (NULL, io->socket, 0); */ + /* bufferevent_setcb (io->bufev, canReadWrapper, didWriteWrapper, */ + /* gotErrorWrapper, io); */ + /* io->bufev = bufferevent_new (io->socket, */ + /* canReadWrapper, */ + /* didWriteWrapper, */ + /* gotErrorWrapper, */ + /* io); */ + /* bufferevent_setwatermark (io->bufev, EV_READ, CCNET_PACKET_LENGTH_HEADER, */ + /* CCNET_RDBUF); */ + /* bufferevent_enable (io->bufev, EV_READ | EV_WRITE); */ + + return io; +} + +CcnetPacketIO* +ccnet_packet_io_new_incoming (CcnetSession *session, + struct sockaddr_storage *addr, + evutil_socket_t socket) +{ + return ccnet_packet_io_new (session, addr, TRUE, socket); +} + + +CcnetPacketIO* +ccnet_packet_io_new_outgoing (CcnetSession *session, + const char *addr_str, + uint16_t port) +{ + struct sockaddr_storage addr; + evutil_socket_t socket; + + if (sock_pton(addr_str, port, &addr) < 0) { + ccnet_warning ("wrong addresss format %s\n", addr_str); + return NULL; + } + + socket = ccnet_net_open_tcp ((struct sockaddr *)&addr, TRUE); + /*if (socket < 0) + ccnet_warning ("opening tcp connection fails: %s\n", strerror(errno)); */ + + return socket < 0 + ? NULL + : ccnet_packet_io_new (session, &addr, FALSE, socket); +} + + +void +ccnet_packet_io_free (CcnetPacketIO *io) +{ + if (io) { + if (io->handling) { + io->schedule_free = 1; + return; + } + + if (io->addr) + g_free (io->addr); + + io->canRead = NULL; + io->didWrite = NULL; + io->gotError = NULL; + + bufferevent_free (io->bufev); + /* fprintf (stderr, "close fd %d\n", io->socket); */ + /* close (io->socket); */ + g_free (io); + } +} + +CcnetSession* +ccnet_packet_io_get_session (CcnetPacketIO *io) +{ + return io->session; +} + + +void +ccnet_packet_io_try_read (CcnetPacketIO *io) +{ + if(EVBUFFER_LENGTH(io->bufev->input)) + canReadWrapper (io->bufev, io); +} + +void +ccnet_packet_io_set_iofuncs (CcnetPacketIO *io, + ccnet_can_read_cb readcb, + ccnet_did_write_cb writecb, + ccnet_net_error_cb errcb, + void *user_data) +{ + io->canRead = readcb; + io->didWrite = writecb; + io->gotError = errcb; + io->user_data = user_data; +} + +int +ccnet_packet_io_is_incoming (const CcnetPacketIO *c) +{ + return c->is_incoming ? 1 : 0; +} + + +void +ccnet_packet_io_set_timeout_secs (CcnetPacketIO *io, int secs) +{ + io->timeout = secs; + bufferevent_settimeout (io->bufev, io->timeout, io->timeout); + if (secs == 0) /* have to remove the original events */ + bufferevent_disable (io->bufev, EV_READ | EV_WRITE); + bufferevent_enable (io->bufev, EV_READ | EV_WRITE); + + /* struct timeval tv; */ + /* tv.tv_sec = secs; */ + /* tv.tv_usec = 0; */ + + /* if (secs != 0) */ + /* bufferevent_set_timeouts (io->bufev, &tv, NULL); */ + /* else */ + /* bufferevent_set_timeouts (io->bufev, NULL, NULL); */ +} + +void +ccnet_packet_io_write_packet (CcnetPacketIO *io, ccnet_packet *packet) +{ + int len; + + len = packet->header.length + CCNET_PACKET_LENGTH_HEADER; + packet->header.length = htons (packet->header.length); + packet->header.id = htonl (packet->header.id); + bufferevent_write (io->bufev, packet, len); +} diff --git a/net/common/packet-io.h b/net/common/packet-io.h new file mode 100644 index 0000000..8845244 --- /dev/null +++ b/net/common/packet-io.h @@ -0,0 +1,84 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_PACKET_IO_H +#define CCNET_PACKET_IO_H + +#include "packet.h" + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#include +#include +#else +#include +#endif + +/* struct evbuffer; */ +/* for libevent2 */ +struct evbuffer; + +struct bufferevent; +struct CcnetSession; +struct ccnet_packet; + +typedef void (*ccnet_can_read_cb)(struct ccnet_packet *, void* user_data); +typedef void (*ccnet_did_write_cb)(struct bufferevent *, void *); +typedef void (*ccnet_net_error_cb)(struct bufferevent *, short what, void *); + +typedef struct CcnetPacketIO CcnetPacketIO; + +struct CcnetPacketIO +{ + unsigned int is_incoming : 1; + unsigned int handling : 1; /* handling event from this IO */ + unsigned int schedule_free : 1; + + int timeout; + + struct sockaddr *addr; + evutil_socket_t socket; + + struct CcnetSession *session; + + struct bufferevent *bufev; + + ccnet_can_read_cb canRead; + ccnet_did_write_cb didWrite; + ccnet_net_error_cb gotError; + void *user_data; +}; + + + +CcnetPacketIO* +ccnet_packet_io_new_outgoing (struct CcnetSession *session, + const char *addr_str, uint16_t port); + + +CcnetPacketIO* +ccnet_packet_io_new_incoming (struct CcnetSession *session, + struct sockaddr_storage *addr, + evutil_socket_t socket); + + +void ccnet_packet_io_free (CcnetPacketIO *io); + +struct CcnetSession* ccnet_packet_io_get_session (CcnetPacketIO *io); + + +int ccnet_packet_io_reconnect (CcnetPacketIO *io); + +int ccnet_packet_io_is_incoming (const CcnetPacketIO *io); + +void ccnet_packet_io_set_timeout_secs (CcnetPacketIO *io, int secs); + + +void ccnet_packet_io_write_packet (CcnetPacketIO *io, ccnet_packet *packet); + +void ccnet_packet_io_set_iofuncs (CcnetPacketIO *io, + ccnet_can_read_cb readcb, + ccnet_did_write_cb writecb, + ccnet_net_error_cb errcb, + void *user_data); + +#endif diff --git a/net/common/peer-mgr.c b/net/common/peer-mgr.c new file mode 100644 index 0000000..7fd5af2 --- /dev/null +++ b/net/common/peer-mgr.c @@ -0,0 +1,1007 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include +#include +#include +#include + +#include "timer.h" +#include "ccnet-db.h" +#include "net.h" + +#include "peer.h" +#include "session.h" +#include "ccnet-config.h" +#include "peer-mgr.h" +#include "peermgr-message.h" +#include "connect-mgr.h" + +#include "algorithms.h" +#include "utils.h" + +#ifdef CCNET_DAEMON +#include "daemon-session.h" +#endif + +#define DEBUG_FLAG CCNET_DEBUG_PEER +#include "log.h" + +#define NOTIFY_MSEC 5000 /* 5s */ +#define DEFAULT_NOTIFY_INTERVAL 86400 * 2 + +#define SAVING_INTERVAL_MSEC 10000 +#define PEER_GC_TIMEOUT 3*60 +#define PEERDB_NAME "peer-db" + +struct CcnetPeerManagerPriv { + CcnetDB *db; + CcnetTimer *timer; + + /* the list of peers to be resolved */ + GList *resolve_peers; +}; + + +enum { + ADDED_SIG, + DELETING_SIG, + PEER_AUTH_DONE_SIG, + LAST_SIGNAL +}; + +G_DEFINE_TYPE (CcnetPeerManager, ccnet_peer_manager, G_TYPE_OBJECT); + +static guint signals[LAST_SIGNAL] = { 0 }; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_PEER_MANAGER, CcnetPeerManagerPriv)) + +/* static int notify_pulse (CcnetPeerManager *manager); */ +static int open_db (CcnetPeerManager *manager); +static void save_peer_addr(CcnetPeerManager *manager, CcnetPeer *peer); +static void remove_peer_roles(CcnetPeerManager *manager, char *peer_id); +void ccnet_peer_manager_load_peerdb (CcnetPeerManager *manager); + + +static void +ccnet_peer_manager_class_init (CcnetPeerManagerClass *klass) +{ + signals[ADDED_SIG] = + g_signal_new ("peer-added", CCNET_TYPE_PEER_MANAGER, + G_SIGNAL_RUN_LAST, + 0, /* no class singal handler */ + NULL, NULL, /* no accumulator */ + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + signals[DELETING_SIG] = + g_signal_new ("peer-deleting", CCNET_TYPE_PEER_MANAGER, + G_SIGNAL_RUN_LAST, + 0, /* no class singal handler */ + NULL, NULL, /* no accumulator */ + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + signals[PEER_AUTH_DONE_SIG] = + g_signal_new ("peer-auth-done", CCNET_TYPE_PEER_MANAGER, + G_SIGNAL_RUN_LAST, + 0, /* no class singal handler */ + NULL, NULL, /* no accumulator */ + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + + g_type_class_add_private (klass, sizeof (CcnetPeerManagerPriv)); +} + + +static void +ccnet_peer_manager_init (CcnetPeerManager *manager) +{ + manager->priv = GET_PRIV (manager); +} + + +CcnetPeerManager* +ccnet_peer_manager_new (CcnetSession *session) +{ + CcnetPeerManager *manager; + + manager = g_object_new (CCNET_TYPE_PEER_MANAGER, NULL); + + manager->session = session; + + manager->peer_hash = g_hash_table_new (g_str_hash, g_str_equal); + + return manager; +} + +int +ccnet_peer_manager_prepare (CcnetPeerManager *manager) +{ + CcnetPeer *peer; + CcnetSession *session = manager->session; + + /* peer point to myself */ + peer = ccnet_peer_new (session->base.id); + peer->name = (char *)session->base.name; + peer->public_port = session->base.public_port; + peer->port = session->base.public_port; + peer->service_url = session->base.service_url; + peer->pubkey = session->pubkey; + /* set to -1 so it will not be saved in to_string() */ + peer->net_state = -1; + peer->is_self = 1; + peer->manager = manager; + + g_hash_table_insert (manager->peer_hash, peer->id, peer); + session->myself = peer; + + return 0; +} + +void +ccnet_peer_manager_free (CcnetPeerManager *manager) +{ + g_free (manager->peerdb_path); + g_object_unref (manager); +} + +GList * +ccnet_peer_manager_get_peer_list (CcnetPeerManager *manager) +{ + return g_hash_table_get_values (manager->peer_hash); +} + +GList* +ccnet_peer_manager_get_peers_with_role (CcnetPeerManager *manager, + const char *role) +{ + GHashTableIter iter; + gpointer key, value; + CcnetPeer *peer; + GList *list = 0; + + g_hash_table_iter_init (&iter, manager->peer_hash); + while (g_hash_table_iter_next (&iter, &key, &value)) { + peer = value; + if (ccnet_peer_has_role(peer, role)) { + list = g_list_prepend (list, peer); + g_object_ref (peer); + } + } + return list; +} + + +static void +add_peer (CcnetPeerManager *manager, CcnetPeer *peer) +{ + peer->manager = manager; + + g_object_ref (peer); + g_hash_table_insert (manager->peer_hash, peer->id, peer); + + if (!peer->is_self) { + g_signal_emit (manager, signals[ADDED_SIG], 0, peer); + } +} + +void +ccnet_peer_manager_add_peer (CcnetPeerManager *manager, CcnetPeer *peer) +{ + add_peer (manager, peer); + peer->need_saving = 1; +} + +static void +delete_peer(CcnetPeerManager *manager, CcnetPeer *peer) +{ + char *path; + + /* delete peer conf file from peer-db */ + path = g_build_filename(manager->peerdb_path, peer->id, NULL); + + if (g_unlink(path) < 0) + ccnet_warning("delete file %s error\n", path); + + g_hash_table_remove (manager->peer_hash, peer->id); + remove_peer_roles (manager, peer->id); + g_signal_emit (manager, signals[DELETING_SIG], 0, peer); + + g_object_unref (peer); + g_free(path); +} + +void +ccnet_peer_manager_remove_peer (CcnetPeerManager *manager, + CcnetPeer *peer) +{ + ccnet_peer_shutdown (peer); + delete_peer(manager, peer); +} + +CcnetPeer * +ccnet_peer_manager_add_resolve_peer (CcnetPeerManager *manager, + const char *ip_or_domain, + uint16_t port) +{ + CcnetPeer *peer; + + g_return_val_if_fail (ip_or_domain != NULL, NULL); + g_return_val_if_fail (port != 0, NULL); + + peer = ccnet_peer_new (NON_RESOLVED_PEERID); + peer->public_addr = g_strdup (ip_or_domain); + peer->public_port = port; + peer->to_resolve = 1; + peer->manager = manager; + + manager->priv->resolve_peers = g_list_prepend ( + manager->priv->resolve_peers, peer); + g_object_ref (peer); + + ccnet_conn_manager_add_to_conn_list (manager->session->connMgr, + peer); + ccnet_conn_manager_connect_peer (manager->session->connMgr, + peer); + return peer; +} + + +GList * +ccnet_peer_manager_get_resolve_peers (CcnetPeerManager *manager) +{ + GList *ret, *ptr; + + ret = g_list_copy(manager->priv->resolve_peers); + for (ptr = ret; ptr; ptr = ptr->next) { + g_object_ref ((CcnetPeer*)ptr->data); + } + return ret; +} + +/* + This function is intend to do valid check after peer's ID is resolved. + + This function is called when peer's ID is resolved, peer's public + info is fetched, and connection is established. Return TRUE if + start_keepalive() should be called after this function's + return. Return FALSE otherwise. + */ +gboolean +ccnet_peer_manager_on_peer_resolved (CcnetPeerManager *manager, + CcnetPeer *peer) +{ + peer->to_resolve = 0; + ccnet_conn_manager_remove_from_conn_list (manager->session->connMgr, peer); + + save_peer_addr (manager, peer); + CcnetPeer *old_peer; + + old_peer = ccnet_peer_manager_get_peer (manager, peer->id); + if (old_peer) { + if (old_peer->net_state == PEER_CONNECTED) { + /* Actually this can't happen, because the otherside won't let you connect, + * if you already has a connection to it. */ + /* use old peer instead */ + ccnet_warning ("[Peer Resolve] Resolved an already exists peer.\n"); + g_object_unref (old_peer); + return FALSE; + } else { + ccnet_warning ("[Peer Resolve] Resolved an exists peer, replace it.\n"); + ccnet_peer_manager_remove_peer (manager, old_peer); + g_object_unref (old_peer); + } + } + + manager->priv->resolve_peers = g_list_remove ( + manager->priv->resolve_peers, peer); + g_object_unref (peer); + + ccnet_peer_manager_add_peer (manager, peer); + + if (peer->intend_role) { + ccnet_peer_manager_add_role (manager, peer, peer->intend_role); + g_free (peer->intend_role); + } + + if (peer->want_tobe_relay) { + ccnet_peer_manager_add_role (manager, peer, "MyRelay"); + } + return TRUE; +} + +void +ccnet_peer_manager_on_peer_resolve_failed (CcnetPeerManager *manager, + CcnetPeer *peer) +{ + ccnet_peer_shutdown (peer); + ccnet_conn_manager_remove_from_conn_list (manager->session->connMgr, peer); + manager->priv->resolve_peers = g_list_remove ( + manager->priv->resolve_peers, peer); + g_object_unref (peer); +} + + +/* -------- Peer Database Management ---------------- */ + +static void check_db_table (CcnetDB *db) +{ + char *sql; + + sql = "CREATE TABLE IF NOT EXISTS PeerAddr (peer_id CHAR(41) " + "PRIMARY KEY, addr VARCHAR(15), port INTEGER)"; + ccnet_db_query (db, sql); + + sql = "CREATE TABLE IF NOT EXISTS PeerRole (peer_id CHAR(41) PRIMARY KEY," + "roles TEXT, timestamp BIGINT)"; + ccnet_db_query (db, sql); +} + + +static CcnetDB * +open_sqlite_db (CcnetPeerManager *manager) +{ + CcnetDB *db = NULL; + char *db_dir; + char *db_path; + + db_dir = g_build_filename (manager->session->config_dir, "PeerMgr", NULL); + if (checkdir_with_mkdir(db_dir) < 0) { + ccnet_error ("Cannot open db dir %s: %s\n", db_dir, + strerror(errno)); + g_free (db_dir); + return NULL; + } + g_free (db_dir); + + db_path = g_build_filename (manager->session->config_dir, "PeerMgr", + "peermgr.db", NULL); +#ifdef CCNET_SERVER + db = ccnet_db_new_sqlite (db_path); +#else + if (sqlite_open_db (db_path, &db) < 0) + db = NULL; +#endif + g_free (db_path); + + return db; +} + +static int +open_db (CcnetPeerManager *manager) +{ + CcnetDB *db; + + db = open_sqlite_db (manager); + if (!db) + return -1; + manager->priv->db = db; + check_db_table (db); + return 0; +} + +static gboolean load_peer_addr_cb (CcnetDBRow *row, void *data) +{ + CcnetPeer *peer = (CcnetPeer *)data; + char *addr = (char *) ccnet_db_row_get_column_text (row, 0); + int port = ccnet_db_row_get_column_int (row, 1); + + peer->public_addr = g_strdup(addr); + peer->public_port = port; + return FALSE; +} + +static void +load_peer_addr(CcnetPeerManager *manager, CcnetPeer *peer) +{ + char sql[256]; + + if (!peer || !peer->id) + return; + + snprintf (sql, 256, "SELECT addr, port FROM PeerAddr WHERE peer_id='%s'", + peer->id); + ccnet_db_foreach_selected_row (manager->priv->db, sql, + load_peer_addr_cb, peer); +} + +static void +save_peer_addr(CcnetPeerManager *manager, CcnetPeer *peer) +{ + /* + char sql[256]; + + if (!peer || !peer->id) + return; + + if (peer->public_addr) { + snprintf (sql, 256, "REPLACE INTO PeerAddr VALUES ('%s', '%s', '%d')", + peer->id, peer->public_addr, peer->public_port); + } else { + snprintf (sql, 256, "DELETE FROM PeerAddr WHERE peer_id='%s'", + peer->id); + } + ccnet_db_query (manager->priv->db, sql); + */ +} + +static gboolean load_peer_role_cb (CcnetDBRow *row, void *data) +{ + CcnetPeer *peer = (CcnetPeer *)data; + char *roles = (char *) ccnet_db_row_get_column_text (row, 0); + + ccnet_peer_set_roles (peer, roles); + return FALSE; +} + +static void load_peer_role(CcnetPeerManager *manager, CcnetPeer *peer) +{ + char sql[256]; + + if (!peer) return; + + snprintf (sql, 256, "SELECT roles FROM PeerRole" + " where peer_id = '%s'", peer->id); + ccnet_db_foreach_selected_row (manager->priv->db, sql, + load_peer_role_cb, peer); +} + +static CcnetPeer* +_load_peer (CcnetPeerManager *manager, const char *path) +{ + GError *error = NULL; + CcnetPeer *peer; + char *content; + + g_file_get_contents (path, &content, NULL, &error); + if (error != NULL) { + /* ccnet_debug ("Load peer info from %s error: %s\n", */ + /* path, error->message); */ + return NULL; + } + + peer = ccnet_peer_from_string (content); + if (!peer) { + ccnet_warning ("Load peer info from %s error\n", path); + g_free (content); + return NULL; + } + + load_peer_addr (manager, peer); + load_peer_role (manager, peer); + add_peer (manager, peer); + peer->last_down = time(NULL); + g_free (content); + return peer; +} + +void +ccnet_peer_manager_set_peer_public_addr (CcnetPeerManager *manager, + CcnetPeer *peer, + const char *addr, + int port) +{ + int need_save = 0; + if (port == 0) + port = DEFAULT_PORT; + if (!addr) { + if (peer->public_addr) { + g_free (peer->public_addr); + peer->public_addr = NULL; + need_save = 1; + } + } else if (!peer->public_addr || g_strcmp0 (peer->public_addr, addr) != 0){ + g_free (peer->public_addr); + peer->public_addr = g_strdup (addr); + need_save = 1; + } + if (port > 0 && port != peer->public_port) { + peer->public_port = port; + need_save = 1; + } + if (need_save) + save_peer_addr (manager, peer); +} + + +static void +remove_peer_roles(CcnetPeerManager *manager, char *peer_id) +{ + /* + char sql[256]; + + if (!peer_id) + return; + + snprintf (sql, 256, "DELETE FROM PeerRole WHERE peer_id = '%s'", peer_id); + ccnet_db_query (manager->priv->db, sql); + */ +} + + +static void +save_peer_roles (CcnetPeerManager *manager, CcnetPeer *peer) +{ + /* + char sql[512]; + CcnetDB *db = manager->priv->db; + + GString *buf = g_string_new (NULL); + + ccnet_peer_get_roles_str(peer, buf); + snprintf (sql, 512, "REPLACE INTO PeerRole VALUES ('%s', '%s', " + "%"G_GINT64_FORMAT")", + peer->id, buf->str, get_current_time()); + ccnet_db_query (db, sql); + g_string_free (buf, TRUE); + */ +} + +void +ccnet_peer_manager_add_role (CcnetPeerManager *manager, + CcnetPeer *peer, + const char *role) +{ + ccnet_peer_add_role (peer, role); + save_peer_roles (manager, peer); +} + +void ccnet_peer_manager_remove_role (CcnetPeerManager *manager, + CcnetPeer *peer, + const char *role) +{ + ccnet_peer_remove_role (peer, role); + save_peer_roles (manager, peer); +} + +CcnetPeer* +ccnet_peer_manager_load_peer_by_id (CcnetPeerManager *manager, + const char *peer_id) +{ + char path[PATH_MAX]; + + g_return_val_if_fail (strlen(peer_id) == 40, NULL); + + sprintf (path, "%s" G_DIR_SEPARATOR_S "%s", manager->peerdb_path, peer_id); + + return _load_peer (manager, path); +} + +static void prune_peers (CcnetPeerManager *manager) +{ + GList *peers, *ptr; + + peers = g_hash_table_get_values (manager->peer_hash); + for (ptr = peers; ptr; ptr = ptr->next) { + CcnetPeer *peer = ptr->data; + if (peer->is_self) + continue; + if (peer->role_list == NULL) { + ccnet_debug ("Removed peer %s\n", peer->id); + delete_peer (manager, peer); + } + } + + g_list_free (peers); +} + +void +ccnet_peer_manager_load_peerdb (CcnetPeerManager *manager) +{ + const char *dname; + GDir *dp; + char buf[PATH_MAX]; + + manager->peerdb_path = g_build_filename (manager->session->config_dir, + PEERDB_NAME, NULL); + char *peerdb = manager->peerdb_path; + + open_db(manager); + + if (checkdir_with_mkdir(peerdb) < 0) { + ccnet_warning ("Could not open or make peer-db.\n"); + return; + } + + if ((dp = g_dir_open (peerdb, 0, NULL)) == NULL) { + ccnet_warning ("Can't open peer database %s: %s.\n", peerdb, + strerror (errno)); + return; + } + + while ((dname = g_dir_read_name(dp)) != NULL) { + if (strlen(dname) != 40) + continue; + + sprintf (buf, "%s/%s", peerdb, dname); + if (!g_file_test(buf, G_FILE_TEST_IS_REGULAR)) { + ccnet_warning ("%s is not a regular file\n", buf); + continue; + } + + CcnetPeer *peer = _load_peer (manager, buf); + g_object_unref (peer); + } + g_dir_close(dp); + + prune_peers (manager); +} + +CcnetPeer * +ccnet_peer_manager_get_peer (CcnetPeerManager *manager, + const char *peer_id) +{ + CcnetPeer *peer; + + peer = g_hash_table_lookup (manager->peer_hash, peer_id); + if (peer) + g_object_ref (peer); + return peer; +} + +CcnetPeer* +ccnet_peer_manager_get_peer_by_name (CcnetPeerManager *manager, + const char *name) +{ + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, manager->peer_hash); + while (g_hash_table_iter_next (&iter, &key, &value)) { + CcnetPeer *peer = value; + if (peer->name == NULL) + continue; + if (strcmp(name, peer->name) == 0) { + g_object_ref (peer); + return peer; + } + } + + return NULL; +} + +void +ccnet_peer_manager_add_local_peer (CcnetPeerManager *manager, CcnetPeer *peer) +{ + g_return_if_fail (peer->is_local); + + peer->manager = manager; + manager->local_peers = g_list_append (manager->local_peers, peer); + g_object_ref (peer); +} + +void +ccnet_peer_manager_remove_local_peer (CcnetPeerManager *manager, + CcnetPeer *peer) +{ + g_return_if_fail (peer->is_local); + + manager->local_peers = g_list_remove (manager->local_peers, peer); + g_object_unref (peer); +} + + +static void save_peer (CcnetPeerManager *manager, CcnetPeer *peer) +{ + /* + char *path = NULL; + FILE *fp; + + path = g_build_filename (manager->peerdb_path, peer->id, NULL); + + GString *str = ccnet_peer_to_string (peer); + + if ((fp = g_fopen (path, "wb")) == NULL) { + ccnet_warning ("Can't save peer info: %s\n", strerror(errno)); + goto err; + } + + fputs (str->str, fp); + fclose (fp); + +err: + g_string_free (str, TRUE); + g_free (path); + */ +} + +static int save_pulse (void * vmanager) +{ + CcnetPeerManager *manager = vmanager; + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, manager->peer_hash); + while (g_hash_table_iter_next (&iter, &key, &value)) { + CcnetPeer *peer = value; + +#ifdef CCNET_SERVER + /* clean peers in memory */ + if (peer->role_list == NULL) { + if (peer->net_state == PEER_DOWN && !peer->in_shutdown + && !peer->in_connection) { + time_t now = time(NULL); + if (now < peer->last_down + PEER_GC_TIMEOUT) + continue; + g_hash_table_iter_remove (&iter); + g_object_unref (peer); + } + } +#endif + + if (peer->role_list == NULL) + continue; + + if (peer->need_saving) { + ccnet_debug ("[Peer] Saving peer %s(%.8s) to db\n", + peer->name, peer->id); + save_peer (manager, peer); + peer->need_saving = 0; + } + + } + + return TRUE; +} + +void +ccnet_peer_manager_start (CcnetPeerManager *manager) +{ + ccnet_timer_new (save_pulse, manager, SAVING_INTERVAL_MSEC); + + /* manager->priv->notify_timer = ccnet_timer_new ((TimerCB)notify_pulse, */ + /* manager, NOTIFY_MSEC); */ +} + + +static void +shutdown_peer (gpointer key, gpointer value, gpointer user_data) +{ + CcnetPeer *peer = value; + + if (!peer->is_self) + ccnet_peer_shutdown (peer); +} + + +void ccnet_peer_manager_on_exit (CcnetPeerManager *manager) +{ + save_pulse (manager); + g_hash_table_foreach (manager->peer_hash, shutdown_peer, manager); +} + + + + +/* -------- peermgr message handling -------- */ + +#ifdef CCNET_SERVER + +#include "server-session.h" +#include "user-mgr.h" + + +#endif /* CCNET_SERVER */ + + +void +ccnet_peer_manager_send_ready_message (CcnetPeerManager *manager, CcnetPeer *peer) +{ + CcnetMessage *ready_message = NULL; + char buf[256]; + + snprintf (buf, 256, "v%d\n%s\n", PEERMGR_VERSION, SERVICE_READY); + ready_message = ccnet_message_new (manager->session->base.id, + peer->id, IPEERMGR_APP, + buf, 0); + ccnet_send_message (manager->session, ready_message); + ccnet_message_unref (ready_message); +} + +static void +handle_service_ready_message (CcnetPeerManager *manager, + CcnetMessage *msg, + char *body) +{ + CcnetPeer *peer = ccnet_peer_manager_get_peer (manager, msg->from); + peer->is_ready = 1; + ccnet_debug ("[peer] Received ready from peer %s(%.8s)\n", peer->name, peer->id); + + g_object_unref (peer); +} + +static void +notify_peer_role (CcnetPeerManager *manager, CcnetPeer *peer) +{ + ccnet_debug ("[PeerMgr] Notify roles to peer %s(%.8s)\n", peer->name, + peer->id); + + GString *msgbuf = g_string_new(NULL); + g_string_append_printf (msgbuf, "v%d\n%s\n", PEERMGR_VERSION, + ROLE_NOTIFY); + ccnet_peer_get_roles_str (peer, msgbuf); + g_string_append (msgbuf, "\n"); + + CcnetMessage *msg = ccnet_message_new (manager->session->base.id, + peer->id, IPEERMGR_APP, + msgbuf->str, 0); + ccnet_send_message (manager->session, msg); + ccnet_message_unref (msg); + + g_string_free (msgbuf, TRUE); +} + +void +ccnet_peer_manager_notify_peer_role (CcnetPeerManager *manager, CcnetPeer *peer) +{ + /* notify_peer_role (manager, peer); */ +} + +static void +handle_role_notify_message (CcnetPeerManager *manager, + CcnetMessage *msg, + char *body) +{ + ccnet_debug ("[PeerMgr] Receive role notify message\n"); + char *roles, *ptr; + + roles = body; + if ( (ptr = strchr (body, '\n')) == NULL) { + ccnet_message ("[PeerMgr] Parse role notify message error\n"); + return; + } + *ptr = '\0'; + + CcnetPeer *peer = ccnet_peer_manager_get_peer (manager, msg->from); + if (!peer) { + return; + } + + GList *role_list = string_list_parse_sorted (roles, ","); + if (!string_list_sorted_is_equal (role_list, peer->myrole_list)) { + ccnet_peer_set_myroles (peer, roles); + } + string_list_free (role_list); + g_object_unref (peer); +} + +static int +redirect (CcnetPeer *peer) +{ + ccnet_peer_shutdown (peer); + ccnet_debug("[PeerMgr] connect redirect destination %s(%.8s): %s:%d\n", + peer->name, peer->id, peer->redirect_addr, peer->redirect_port); + ccnet_conn_manager_connect_peer (peer->manager->session->connMgr, peer); + g_object_unref (peer); + return FALSE; +} + +static void +schedule_redirect (CcnetPeerManager *manager, CcnetPeer *peer) +{ + g_object_ref (peer); + ccnet_timer_new ((TimerCB)redirect, peer, 1); +} + +static void +handle_redirect_message (CcnetPeerManager *manager, + CcnetMessage *msg, + char *body) +{ + int port; + char *p, *addr; + CcnetPeer *from; + int len; + + ccnet_debug ("[PeerMgr] Receive redirect message from %.8s\n", + msg->from); + + len = strlen(body); + if (body[len-1] != '\n') { + ccnet_message ("[PeerMgr] Bad formatted redirect msg: not end with '\\n'\n"); + return; + } + body[len-1] = '\0'; + + addr = body; + if ( (p = strchr(body, ':')) == NULL) { + ccnet_message ("[PeerMgr] Bad formatted redirect msg: port missing\n"); + return; + } + *p = '\0'; + port = atoi(p+1); + if (port <= 0) { + ccnet_message ("[PeerMgr] Bad formatted redirect msg: wrong port\n"); + return; + } + + + from = ccnet_peer_manager_get_peer (manager, msg->from); + ccnet_peer_set_redirect (from, addr, port); + /* note: can shutdown the connection during this event iteration, + * must schedule it */ + schedule_redirect (manager, from); + + g_object_unref (from); +} + +void +ccnet_peer_manager_receive_message (CcnetPeerManager *manager, + CcnetMessage *msg) +{ + guint16 version; + char *type; + char *body; + + if (parse_peermgr_message (msg, &version, &type, &body) < 0) { + ccnet_message ("Invalid peermgr message from %.8s\n", msg->from); + return; + } + + if (version != PEERMGR_VERSION) { + ccnet_message ("Incompatible peermgr message version %d from %.8s\n", + version, msg->from); + return; + } + + if (strcmp(type, ROLE_NOTIFY) == 0) + handle_role_notify_message (manager, msg, body); + else if (strcmp(type, SERVICE_READY) == 0) + handle_service_ready_message (manager, msg, body); + else if (strcmp(type, PEER_REDIRECT) == 0) + handle_redirect_message (manager, msg, body); +} + + +static void +send_redirect_message (CcnetPeerManager *manager, CcnetPeer *peer, + CcnetPeer *to) +{ + CcnetMessage *msg = NULL; + char buf[256]; + + if (!to->public_addr || to->public_port == 0) { + ccnet_warning ("[peer] Redirect to peer %s(%.8s) has an invalid address\n", + to->name, to->id); + return; + } + snprintf (buf, 256, "v%d\n%s\n%s:%d\n", PEERMGR_VERSION, PEER_REDIRECT, + to->public_addr, to->public_port); + + msg = ccnet_message_new (manager->session->base.id, + peer->id, IPEERMGR_APP, + buf, 0); + ccnet_send_message (manager->session, msg); + ccnet_message_unref (msg); +} + +void +ccnet_peer_manager_redirect_peer (CcnetPeerManager *manager, + CcnetPeer *peer, + CcnetPeer *to) +{ + ccnet_debug ("[PeerMgr] redirect peer %s(%.8s) to %s(%.8s)\n", + peer->name, peer->id, + to->name, to->id); + send_redirect_message (manager, peer, to); + + return; +} + +void +ccnet_peer_manager_on_peer_session_key_sent (CcnetPeerManager *manager, + CcnetPeer *peer) +{ + g_signal_emit_by_name (manager, "peer-auth-done", peer); +} + +void +ccnet_peer_manager_on_peer_session_key_received (CcnetPeerManager *manager, + CcnetPeer *peer) +{ + g_signal_emit_by_name (manager, "peer-auth-done", peer); +} + diff --git a/net/common/peer-mgr.h b/net/common/peer-mgr.h new file mode 100644 index 0000000..9f6d217 --- /dev/null +++ b/net/common/peer-mgr.h @@ -0,0 +1,129 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_PEER_MGR_H +#define CCNET_PEER_MGR_H + +#include +#include + +#include "peer.h" + +#define CCNET_TYPE_PEER_MANAGER (ccnet_peer_manager_get_type ()) +#define CCNET_PEER_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_PEER_MANAGER, CcnetPeerManager)) +#define CCNET_IS_PEER_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_PEER_MANAGER)) +#define CCNET_PEER_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_PEER_MANAGER, CcnetPeerManagerClass)) +#define CCNET_IS_PEER_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_PEER_MANAGER)) +#define CCNET_PEER_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_PEER_MANAGER, CcnetPeerManagerClass)) + + +typedef struct _CcnetPeerManager CcnetPeerManager; +typedef struct _CcnetPeerManagerClass CcnetPeerManagerClass; + +typedef struct CcnetPeerManagerPriv CcnetPeerManagerPriv; + +struct _CcnetPeerManager +{ + GObject parent_instance; + + CcnetSession *session; + + char *peerdb_path; + + GHashTable *peer_hash; + + GList *local_peers; + + guint32 connected_peer; + + CcnetPeerManagerPriv *priv; +}; + +struct _CcnetPeerManagerClass +{ + GObjectClass parent_class; +}; + +GType ccnet_peer_manager_get_type (void); + +CcnetPeerManager* ccnet_peer_manager_new (CcnetSession *); + +int +ccnet_peer_manager_prepare (CcnetPeerManager *manager); + +void ccnet_peer_manager_free (CcnetPeerManager *manager); + +void ccnet_peer_manager_start (CcnetPeerManager *manager); +void ccnet_peer_manager_on_exit (CcnetPeerManager *manager); + +void ccnet_peer_manager_add_peer (CcnetPeerManager *manager, CcnetPeer *peer); +void ccnet_peer_manager_remove_peer (CcnetPeerManager *manager, CcnetPeer *peer); + +CcnetPeer* ccnet_peer_manager_get_peer (CcnetPeerManager *manager, + const char *peer_id); + +CcnetPeer* ccnet_peer_manager_get_peer_by_name (CcnetPeerManager *manager, + const char *name); + +GList* ccnet_peer_manager_get_peer_list (CcnetPeerManager *manager); +GList* ccnet_peer_manager_get_peers_with_role (CcnetPeerManager *manager, + const char *role); + +void ccnet_peer_manager_add_role (CcnetPeerManager *manager, + CcnetPeer *peer, + const char *role); + +void ccnet_peer_manager_remove_role (CcnetPeerManager *manager, + CcnetPeer *peer, + const char *role); + +void ccnet_peer_manager_add_local_peer (CcnetPeerManager *manager, + CcnetPeer *peer); +void ccnet_peer_manager_remove_local_peer (CcnetPeerManager *manager, + CcnetPeer *peer); + +struct _CcnetMessage; +void ccnet_peer_manager_receive_message (CcnetPeerManager *manager, + struct _CcnetMessage *msg); + +void +ccnet_peer_manager_notify_peer_role (CcnetPeerManager *manager, + CcnetPeer *peer); + +void +ccnet_peer_manager_set_peer_public_addr (CcnetPeerManager *manager, + CcnetPeer *peer, + const char *addr, + int port); + +#ifdef CCNET_SERVER +void +ccnet_peer_manager_send_bind_status (CcnetPeerManager *manager, + const char *peer_id, + const char *result); +#endif /* CCNET_SERVER */ + +/* function of resolving peer */ +CcnetPeer * +ccnet_peer_manager_add_resolve_peer (CcnetPeerManager *manager, + const char *ip_or_domain, + uint16_t port); + +GList * +ccnet_peer_manager_get_resolve_peers (CcnetPeerManager *manager); + +void +ccnet_peer_manager_redirect_peer (CcnetPeerManager *manager, + CcnetPeer *peer, + CcnetPeer *to); + +void +ccnet_peer_manager_send_ready_message (CcnetPeerManager *manager, CcnetPeer *peer); + +void +ccnet_peer_manager_on_peer_session_key_sent (CcnetPeerManager *manager, + CcnetPeer *peer); +void +ccnet_peer_manager_on_peer_session_key_received (CcnetPeerManager *manager, + CcnetPeer *peer); + +#endif diff --git a/net/common/peer.c b/net/common/peer.c new file mode 100644 index 0000000..599f5ec --- /dev/null +++ b/net/common/peer.c @@ -0,0 +1,1199 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#include +#include +#include +#else +#include +#endif + +#include +#include + +#ifdef WIN32 + #include +#else + #include + #include +#endif +#include "net.h" + +#include + +#include "timer.h" + +#include "peer.h" +#include "packet-io.h" + +#include "rsa.h" + +#include "session.h" +#include "peer-mgr.h" +#include "perm-mgr.h" +#include "processor.h" +#include "proc-factory.h" +#include "processors/service-proxy-proc.h" +#include "connect-mgr.h" + +#include "utils.h" + +#define DEBUG_FLAG CCNET_DEBUG_PEER +#include "log.h" + + +enum { + DOWN_SIG, /* connection down */ + AUTH_DONE_SIG, /* peer become reachable and the auth state + is AUTH_FULL, see keepalive-proc */ + + AUTH_UPDATED_SIG, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +#define OBJECT_TYPE_STRING "peer" + +#include "../lib/peer-common.h" + +void ccnet_peer_set_net_state (CcnetPeer *peer, int net_state); +static void ccnet_peer_finalize (GObject *object); + +static void shutdown_processors (CcnetPeer *peer); + +static void +set_property (GObject *object, guint property_id, + const GValue *v, GParamSpec *pspec) +{ + set_property_common (object, property_id, v, pspec); +} + +void +ccnet_peer_finalize (GObject *object) +{ + CcnetPeer *peer = CCNET_PEER (object); + + g_free (peer->name); + g_free (peer->addr_str); + g_free (peer->service_url); + g_hash_table_unref (peer->processors); + g_free (peer->session_key); + evbuffer_free (peer->packet); + + if (peer->pubkey) + RSA_free (peer->pubkey); + + G_OBJECT_CLASS(ccnet_peer_parent_class)->finalize (object); +} + +void +ccnet_peer_free (CcnetPeer *peer) +{ + ccnet_peer_shutdown (peer); + g_object_unref (peer); +} + +static void +ccnet_peer_class_init (CcnetPeerClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = ccnet_peer_finalize; + gobject_class->get_property = get_property; + gobject_class->set_property = set_property; + + define_properties (gobject_class); + + signals[AUTH_DONE_SIG] = + g_signal_new ("auth-done", CCNET_TYPE_PEER, + G_SIGNAL_RUN_LAST, + 0, /* no class singal handler */ + NULL, NULL, /* no accumulator */ + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[DOWN_SIG] = + g_signal_new ("down", CCNET_TYPE_PEER, + G_SIGNAL_RUN_LAST, + 0, /* no class singal handler */ + NULL, NULL, /* no accumulator */ + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[AUTH_UPDATED_SIG] = + g_signal_new ("auth-updated", CCNET_TYPE_PEER, + G_SIGNAL_RUN_LAST, + 0, /* no class singal handler */ + NULL, NULL, /* no accumulator */ + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + +} + +static void +ccnet_peer_init (CcnetPeer *peer) +{ + +} + + +CcnetPeer* +ccnet_peer_new (const char *id) +{ + CcnetPeer *peer; + + g_return_val_if_fail (strlen(id) == 40, NULL); + + peer = g_object_new (CCNET_TYPE_PEER, NULL); + memcpy (peer->id, id, 40); + peer->id[40] = '\0'; + + peer->net_state = PEER_DOWN; + peer->public_port = 0; + + peer->processors = g_hash_table_new_full (g_direct_hash, g_direct_equal, + NULL, NULL); + peer->reqID = CCNET_USER_ID_START; + + peer->packet = evbuffer_new (); + + return peer; +} + + +inline static void append_string_property (GString *buf, const char *name, + const char *value) +{ + if (value && value[0]) + g_string_append_printf (buf, "%s %s\n", name, value); +} + +inline static void append_int_property (GString *buf, const char *name, + int value) +{ + if (value != -1) + g_string_append_printf (buf, "%s %d\n", name, value); +} + +inline static void append_int64_property (GString *buf, const char *name, + gint64 value) +{ + g_string_append_printf (buf, "%s %"G_GINT64_FORMAT"\n", name, value); +} + + +GString* +ccnet_peer_to_string (CcnetPeer *peer) +{ + GString *buf = g_string_new (NULL); + g_string_append (buf, "peer/"); + g_string_append (buf, peer->id); + g_string_append (buf, "\n"); + + append_string_property (buf, "name", peer->name); + append_string_property (buf, "service-url", peer->service_url); + + if (peer->pubkey) { + GString *str = public_key_to_gstring(peer->pubkey); + g_string_append_printf (buf, "%s %s\n", "pubkey", str->str); + g_string_free(str, TRUE); + } + + return buf; +} + + +static void parse_field (CcnetPeer *peer, const char *key, char *value) +{ + if (strcmp(key, "name") == 0) { + g_free (peer->name); + peer->name = g_strdup(value); + return; + } + + if (strcmp(key, "service-url") == 0) { + g_free (peer->service_url); + peer->service_url = g_strdup(value); + return; + } + + if (strcmp(key, "pubkey") == 0) { + if (peer->pubkey) + RSA_free(peer->pubkey); + peer->pubkey = public_key_from_string (value); + return; + } +} + +CcnetPeer* +ccnet_peer_from_string (char *content) +{ + CcnetPeer *peer = NULL; + + char *ptr, *start = content; + + if ( !(ptr = strchr(start, '\n')) ) return NULL; + *ptr = '\0'; + char *object_id = start; + start = ptr + 1; + + char *object_type = ccnet_object_type_from_id (object_id); + if (g_strcmp0(object_type, OBJECT_TYPE_STRING) != 0) + goto out; + + char *pure_id = object_id + strlen(object_type) + 1; + if (!peer_id_valid(pure_id)) { + ccnet_warning ("Wrong peer id %s\n", pure_id); + goto out; + } + + peer = ccnet_peer_new (pure_id); + parse_key_value_pairs ( + start, (KeyValueFunc)parse_field, peer); + +out: + g_free (object_type); + return peer; +} + +void +ccnet_peer_update_from_string (CcnetPeer *peer, char *content) +{ + char *ptr, *start = content; + + if ( !(ptr = strchr(start, '\n')) ) return; + *ptr = '\0'; + char *object_id = start; + start = ptr + 1; + + char *object_type = ccnet_object_type_from_id (object_id); + if (strcmp(object_type, OBJECT_TYPE_STRING) != 0) + goto out; + + char *pure_id = object_id + strlen(object_type) + 1; + g_return_if_fail (strcmp(pure_id, peer->id) == 0); + + parse_key_value_pairs ( + start, (KeyValueFunc)parse_field, peer); + + peer->need_saving = 1; +out: + g_free (object_type); +} + +void +ccnet_peer_set_net_state (CcnetPeer *peer, int net_state) +{ + /* do not need saving */ + + if (peer->net_state == net_state) + return; + + ccnet_debug ("[Peer] Peer %s(%.8s) net state changed: %s->%s\n", + peer->name, peer->id, + ccnet_peer_get_net_state_string(peer->net_state), + ccnet_peer_get_net_state_string(net_state)); + peer->last_net_state = peer->net_state; + + if (net_state == PEER_DOWN) { + if (!peer->is_local) + --peer->manager->connected_peer; + } else + if (!peer->is_local) + ++peer->manager->connected_peer; + + if (net_state == PEER_CONNECTED && !peer->io->is_incoming) + g_object_set (peer, "can-connect", 1, NULL); + + g_object_set (peer, "net-state", net_state, NULL); +} + +static void +ccnet_peer_set_addr_str (CcnetPeer *peer, const char *addr_str) +{ + /* do not need saving */ + if (!addr_str) + return; + if (peer->addr_str && strcmp(addr_str, peer->addr_str) == 0) + return; + + g_object_set (peer, "ip", addr_str, NULL); + ccnet_debug ("[Peer] Updated peer %s(%.10s) address %s\n", + peer->name, peer->id, addr_str); +} + +void +ccnet_peer_update_address (CcnetPeer *peer, const char *addr_str, + unsigned short port) +{ + if (!is_valid_ipaddr (addr_str)) + return; + + ccnet_peer_set_addr_str (peer, addr_str); + + if (port == 0) + return; + peer->port = port; +} + +void ccnet_peer_set_pubkey (CcnetPeer *peer, char *str) +{ + g_object_set (peer, "pubkey", str, NULL); + if (!peer->pubkey) + ccnet_warning("Wrong public key format\n"); + peer->need_saving = 1; +} + +int +ccnet_peer_prepare_channel_encryption (CcnetPeer *peer) +{ + if (!peer->session_key) + return -1; + + if ( ccnet_generate_cipher(peer->session_key, strlen(peer->session_key), + peer->key, peer->iv) < 0) + return -1; + + peer->encrypt_channel = 1; + return 0; +} + +/* -------- role management -------- */ + +void +ccnet_peer_add_role (CcnetPeer *peer, const char *role) +{ + if (!ccnet_peer_has_role(peer, role)) { + peer->role_list = string_list_append_sorted ( + peer->role_list, role); + } +} + +void +ccnet_peer_remove_role (CcnetPeer *peer, const char *role) +{ + g_return_if_fail (role != NULL); + + if (!string_list_is_exists(peer->role_list, role)) + return; + + peer->role_list = string_list_remove (peer->role_list, role); +} + +gboolean +ccnet_peer_has_role (CcnetPeer *peer, const char *role) +{ + return string_list_is_exists(peer->role_list, role); +} + +gboolean +ccnet_peer_has_my_role (CcnetPeer *peer, const char *role) +{ + return string_list_is_exists(peer->myrole_list, role); +} + +void +ccnet_peer_set_roles (CcnetPeer *peer, const char *roles) +{ + GList *role_list = string_list_parse_sorted (roles, ","); + string_list_free (peer->role_list); + peer->role_list = role_list; +} + +void +ccnet_peer_set_myroles (CcnetPeer *peer, const char *roles) +{ + GList *role_list = string_list_parse_sorted (roles, ","); + string_list_free (peer->myrole_list); + peer->myrole_list = role_list; + + /* ccnet_debug ("[Peer] Myrole on %s(%.8s) is set to %s\n", */ + /* peer->id, peer->name, roles); */ +} + +void +ccnet_peer_get_roles_str (CcnetPeer *peer, GString* buf) +{ + string_list_join (peer->role_list, buf, ","); +} + +void +ccnet_peer_get_myroles_str (CcnetPeer *peer, GString* buf) +{ + string_list_join (peer->myrole_list, buf, ","); +} + + + +/* ----------- Packet Handling & Networking --------------------- */ + +static void remove_write_callbacks (CcnetPeer *peer) +{ + g_list_foreach (peer->write_cbs, (GFunc)g_free, NULL); + g_list_free (peer->write_cbs); +} + +static void +_peer_shutdown (CcnetPeer *peer) +{ + peer->in_shutdown = 1; + + if (peer->net_state == PEER_CONNECTED) { + peer->last_down = time(NULL); + ccnet_packet_io_free (peer->io); + peer->io = NULL; + g_object_set (peer, "can-connect", 0, NULL); + } + peer->is_ready = 0; + g_free (peer->dns_addr); + peer->dns_addr = NULL; + peer->dns_done = 0; + + /* clear session key when peer down */ + peer->encrypt_channel = 0; + g_free (peer->session_key); + peer->session_key = NULL; + + ccnet_debug ("Shutdown all processors for peer %s\n", peer->name); + shutdown_processors (peer); + remove_write_callbacks (peer); + + ccnet_peer_set_net_state (peer, PEER_DOWN); + + g_signal_emit (peer, signals[DOWN_SIG], 0); + + peer->in_shutdown = 0; +} + +int +shutdown_peer (CcnetPeer *peer) +{ + _peer_shutdown (peer); + peer->shutdown_scheduled = 0; + g_object_unref (peer); + return FALSE; +} + + +static void +schedule_shutdown (CcnetPeer *peer) +{ + if (peer->shutdown_scheduled) + return; + + g_object_ref (peer); + ccnet_timer_new ((TimerCB)shutdown_peer, peer, 1); + peer->shutdown_scheduled = 1; +} + +void +ccnet_peer_shutdown (CcnetPeer *peer) +{ + schedule_shutdown (peer); +} + +void +ccnet_peer_shutdown_no_delay (CcnetPeer *peer) +{ + if (!peer->shutdown_scheduled) { + peer->shutdown_scheduled = 1; + _peer_shutdown (peer); + peer->shutdown_scheduled = 0; + } +} + +static void +create_remote_processor (CcnetPeer *peer, CcnetPeer *remote_peer, + int req_id, int argc, char **argv) +{ + CcnetProcessor *processor; + CcnetProcFactory *factory = peer->manager->session->proc_factory; + + processor = ccnet_proc_factory_create_slave_processor ( + factory,"service-proxy", peer, req_id); + ccnet_processor_start (processor, 0, NULL); + + ccnet_service_proxy_invoke_remote (processor, remote_peer, argc, argv); +} + +static void +create_local_processor (CcnetPeer *peer, int req_id, int argc, char **argv) +{ + CcnetProcessor *processor; + CcnetProcFactory *factory = peer->manager->session->proc_factory; + + processor = ccnet_proc_factory_create_slave_processor ( + factory, argv[0], peer, req_id); + + if (processor) { + ccnet_processor_start (processor, argc-1, argv+1); + } else { + CcnetService *service; + + service = ccnet_session_get_service (peer->manager->session, argv[0]); + if (service != NULL) { + processor = ccnet_proc_factory_create_slave_processor ( + factory, "service-proxy", peer, req_id); + ccnet_processor_start (processor, 0, NULL); + ccnet_service_proxy_invoke_local (processor, service->provider, + argc, argv); + } else { + ccnet_peer_send_response (peer, req_id, SC_UNKNOWN_SERVICE, + SS_UNKNOWN_SERVICE, + NULL, 0); + ccnet_debug ("Unknown service %s invoke by %s(%.8s)\n", + argv[0], peer->name, peer->id); + } + } +} + +static void create_processor (CcnetPeer *peer, int req_id, + int argc, char **argv) +{ + CcnetSession *session = peer->manager->session; + + if (strcmp(argv[0], "remote") == 0) { + /* we have check this before (in permission checking) */ + CcnetPeer *remote_peer; + + remote_peer = ccnet_peer_manager_get_peer (peer->manager, argv[1]); + if (!remote_peer) { + ccnet_peer_send_response (peer, req_id, SC_UNKNOWN_PEER, + SS_UNKNOWN_PEER, NULL, 0); + ccnet_warning ("Unknown remote peer in invoking remote service\n"); + return; + } + /* if (remote_peer->net_state == PEER_DOWN) { */ + /* ccnet_peerSendResponse (peer, req_id, SC_PEER_UNREACHABLE, */ + /* SS_PEER_UNREACHABLE, NULL, 0); */ + /* ccnet_warning ("Unreachable remote peer in invoking remote service\n"); */ + /* return; */ + /* } */ + + /* To simplify caller's logic, we allow starting a remote processor to + * local host. Translate this call into a local one. + */ + if (session->myself == remote_peer) { + create_local_processor (peer, req_id, argc-2, argv+2); + g_object_unref (remote_peer); + return; + } + + create_remote_processor (peer, remote_peer, req_id, argc-2, argv+2); + g_object_unref (remote_peer); + return; + } + + create_local_processor (peer, req_id, argc, argv); +} + +static void +handle_request (CcnetPeer *peer, int req_id, char *data, int len) +{ + char *msg; + gchar **commands; + gchar **pcmd; + int i, perm; + + /* TODO: remove string copy */ + if (len < 1) + return; + msg = g_malloc (len+1); + memcpy (msg, data, len); + msg[len] = '\0'; + + commands = g_strsplit_set (msg, " \t", 10); + for (i=0, pcmd = commands; *pcmd; pcmd++) + i++; + if (i <= 0) return; + g_free (msg); + + /* permission checking */ + if (!peer->is_local) { + perm = ccnet_perm_manager_check_permission(peer->manager->session->perm_mgr, + peer, commands[0], + req_id, + i, commands); + if (perm == PERM_CHECK_ERROR) { + ccnet_peer_send_response (peer, req_id, SC_PERM_ERR, SS_PERM_ERR, + NULL, 0); + goto ret; + } else if (perm == PERM_CHECK_DELAY) { + ccnet_peer_send_response (peer, req_id, SC_PERM_ERR, SS_PERM_ERR, + NULL, 0); + goto ret; + } else if (perm == PERM_CHECK_NOSERVICE) { + ccnet_peer_send_response (peer, req_id, SC_UNKNOWN_SERVICE_IN_PERM, + SS_UNKNOWN_SERVICE_IN_PERM, NULL, 0); + goto ret; + } + } + + /* check duplication request */ + CcnetProcessor *processor; + processor = ccnet_peer_get_processor (peer, SLAVE_ID(req_id)); + if (processor != NULL) { + ccnet_warning ("Received duplication request, id is %d\n", req_id); + goto ret; + } + + create_processor (peer, req_id, i, commands); + +ret: + g_strfreev (commands); +} + +static void +handle_response (CcnetPeer *peer, int req_id, char *data, int len) +{ + CcnetProcessor *processor; + char *code, *code_msg = 0, *content = 0; + int clen; + char *ptr, *end; + + if (len < 4) + goto error; + + code = data; + + ptr = data + 3; + if (*ptr == '\n') { + /* no code_msg */ + *ptr++ = '\0'; + content = ptr; + clen = len - (ptr - data); + goto parsed; + } + + if (*ptr != ' ') + goto error; + + *ptr++ = '\0'; + code_msg = ptr; + + end = data + len; + for (ptr = data; *ptr != '\n' && ptr != end; ptr++) ; + + if (ptr == end) /* must end with '\n' */ + goto error; + + /* if (*(ptr-1) == '\r') */ + /* *(ptr-1) = '\0'; */ + *ptr++ = '\0'; + content = ptr; + clen = len - (ptr - data); + +parsed: + processor = ccnet_peer_get_processor (peer, MASTER_ID (req_id)); + if (processor == NULL) { + /* do nothing if receiving SC_PROC_DEAD and the processor on + * this side is also not present. Otherwise send SC_PROC_DEAD + */ + if (memcmp(code, SC_PROC_DEAD, 3) != 0) { + ccnet_debug ("Delayed response from %s(%.10s), id is %d, %s %s\n", + peer->name, peer->id, req_id, code, code_msg); + ccnet_peer_send_update (peer, req_id, + SC_PROC_DEAD, SS_PROC_DEAD, + NULL, 0); + } + return; + } + /* if (!peer->is_local) */ + /* ccnet_debug ("[RECV] handle_response %s id is %d, %s %s\n", */ + /* GET_PNAME(processor), PRINT_ID(processor->id), */ + /* code, code_msg); */ + + peer->in_processor_call = 1; + ccnet_processor_handle_response (processor, code, code_msg, content, clen); + peer->in_processor_call = 0; + return; + +error: + ccnet_warning ("Bad response format from %s\n", peer->id); +} + +static void +handle_update (CcnetPeer *peer, int req_id, char *data, int len) +{ + CcnetProcessor *processor; + char *code, *code_msg = 0, *content = 0; + int clen; + char *ptr, *end; + + if (len < 4) + goto error; + + code = data; + + ptr = data + 3; + if (*ptr == '\n') { + /* no code_msg */ + *ptr++ = '\0'; + content = ptr; + clen = len - (ptr - data); + goto parsed; + } + + if (*ptr != ' ') + goto error; + + *ptr++ = '\0'; + code_msg = ptr; + + end = data + len; + for (ptr = data; *ptr != '\n' && ptr != end; ptr++) ; + + if (ptr == end) /* must end with '\n' */ + goto error; + + /* if (*(ptr-1) == '\r') */ + /* *(ptr-1) = '\0'; */ + *ptr++ = '\0'; + content = ptr; + clen = len - (ptr - data); + +parsed: + processor = ccnet_peer_get_processor (peer, SLAVE_ID(req_id)); + if (processor == NULL) { + if (memcmp(code, SC_PROC_DEAD, 3) != 0 + && memcmp(code, SC_PROC_DONE, 3) != 0) { + ccnet_debug ("Delayed update from %s(%.8s), id is %d, %s %s\n", + peer->name, peer->id, req_id, code, code_msg); + ccnet_peer_send_response (peer, req_id, + SC_PROC_DEAD, SS_PROC_DEAD, + NULL, 0); + } + return; + } + + /* if (!peer->is_local) */ + /* ccnet_debug ("[RECV] handle_update %s id is %d, %s %s\n", */ + /* GET_PNAME(processor), PRINT_ID(processor->id), */ + /* code, code_msg); */ + peer->in_processor_call = 1; + ccnet_processor_handle_update (processor, code, code_msg, content, clen); + peer->in_processor_call = 0; + return; + +error: + ccnet_warning ("Bad update format from %s\n", peer->id); +} + + +static void +handle_packet (ccnet_packet *packet, CcnetPeer *peer) +{ + switch (packet->header.type) { + case CCNET_MSG_REQUEST: + handle_request (peer, packet->header.id, + packet->data, packet->header.length); + break; + case CCNET_MSG_RESPONSE: + handle_response (peer, packet->header.id, + packet->data, packet->header.length); + break; + case CCNET_MSG_UPDATE: + handle_update (peer, packet->header.id, + packet->data, packet->header.length); + break; + default: + ccnet_warning ("Unknown header type %d\n", packet->header.type); + }; +} + +static void +canRead (ccnet_packet *packet, void *vpeer) +{ + CcnetPeer *peer = vpeer; + g_object_ref (peer); + + /* if (!peer->is_local) */ + /* ccnet_debug ("[RECV] Recieve packat from %s type is %d, id is %d\n", */ + /* peer->id, packet->header.type, packet->header.id); */ + + if (packet->header.id == 0) + return; + + if (packet->header.type != CCNET_MSG_ENCPACKET) { + handle_packet (packet, peer); + } else { + /* ccnet_debug ("receive an encrypt packet\n"); */ + + if (!peer->session_key) { + ccnet_debug("Receive a encrypted packet from %s(%.8s) while " + "not having session key \n", peer->name, peer->id); + goto out; + } + + char *data; + int len; + int ret; + ret = ccnet_decrypt_with_key (&data, &len, packet->data, packet->header.id, + peer->key, peer->iv); + if (ret < 0) + ccnet_warning ("[SEND] decryption error for peer %s(%.8s) \n", + peer->name, peer->id); + else { + ccnet_packet *new_pac = (ccnet_packet *)data; + /* byte order, from network to host */ + new_pac->header.length = ntohs(new_pac->header.length); + new_pac->header.id = ntohl (new_pac->header.id); + + handle_packet (new_pac, peer); + g_free (data); + } + } + +out: + g_object_unref (peer); +} + +struct WriteCallback { + int removing : 1; + + PeerWriteCallback func; + void *user_data; +}; + +void +ccnet_peer_add_write_callback (CcnetPeer *peer, + PeerWriteCallback func, + void *user_data) +{ + if (peer->net_state == PEER_CONNECTED) { + struct WriteCallback *wcb = g_new0 (struct WriteCallback, 1); + wcb->func = func; + wcb->user_data = user_data; + + peer->write_cbs = g_list_prepend (peer->write_cbs, wcb); + } else { + ccnet_warning ("add_write_callback error: Peer not reachable\n"); + } +} + +void +ccnet_peer_remove_write_callback (CcnetPeer *peer, + PeerWriteCallback func, + void *user_data) +{ + GList *ptr; + + for (ptr = peer->write_cbs; ptr; ptr = ptr->next) { + struct WriteCallback *wcb = ptr->data; + if (wcb->func == func && wcb->user_data == user_data) { + peer->write_cbs = g_list_delete_link (peer->write_cbs, ptr); + g_free (wcb); + return; + } + } +} + + +static void +didWrite(struct bufferevent * evin, void * vpeer) +{ + CcnetPeer *peer = vpeer; + GList *ptr; + + g_object_ref (peer); + peer->in_writecb = 1; + + for (ptr = peer->write_cbs; ptr; ) { + struct WriteCallback *wcb = ptr->data; + GList *cur = ptr; + ptr = ptr->next; + if (wcb->func(peer, wcb->user_data) == FALSE) { + peer->write_cbs = g_list_delete_link (peer->write_cbs, cur); + g_free (wcb); + } + } + + peer->in_writecb = 0; + g_object_unref (peer); +} + + + +static void +gotError (struct bufferevent *evbuf, short what, void *vpeer) +{ + CcnetPeer *peer = vpeer; + g_object_ref (peer); + /* ccnet_warning ("libevent got an error on peer %s what==%d, errno=%d (%s)\n", */ + /* peer->name, (int)what, errno, strerror(errno)); */ + + if (what & EVBUFFER_TIMEOUT) { + ccnet_warning ("libevent got a timeout for peer %s(%.8s), what=%hd, timeout secs=%ld\n", + peer->name, peer->id, what, *((time_t *)&(evbuf->timeout_read))); + ccnet_message ("Peer %s (%.10s) down for timeout\n", peer->name, peer->id); + peer->num_fails++; + ccnet_peer_shutdown (peer); + } + + if (what & (EVBUFFER_EOF | EVBUFFER_ERROR)) { + if (what & EVBUFFER_ERROR) + ccnet_warning ("libevent got an error! what=%hd, errno=%d (%s)\n", + what, errno, strerror(errno)); + if (peer->is_local) { + ccnet_message ("Local peer down\n"); + ccnet_peer_shutdown (peer); + ccnet_session_unregister_service (peer->manager->session, peer); + ccnet_peer_manager_remove_local_peer (peer->manager, + peer); + } else { + ccnet_message ("[Net Error] Peer %s (%.10s) down\n", peer->name, peer->id); + peer->num_fails++; + ccnet_peer_shutdown (peer); + } + } + + g_object_unref (peer); +} + +/* static void */ +/* ccnet_peer_reset_io (CcnetPeer *peer) */ +/* { */ +/* ccnet_packet_io_set_timeout_secs (peer->io, 0); /\* disable timeout *\/ */ +/* ccnet_packet_io_set_iofuncs (peer->io, canRead, didWrite, gotError, peer); */ +/* } */ + +void +ccnet_peer_set_io (CcnetPeer *peer, CcnetPacketIO *io) +{ + peer->io = io; + /* libevent remove a previous timeout seems not work in libevent 2.0, + so we have to disable timeout by set it to a large value */ + if (!peer->is_local) + ccnet_packet_io_set_timeout_secs (peer->io, 10000); + ccnet_packet_io_set_iofuncs (peer->io, canRead, didWrite, gotError, peer); +} + + +int +ccnet_peer_get_request_id (CcnetPeer *peer) +{ + return (++peer->reqID); +} + + +#undef DEBUG_FLAG +/* #define DEBUG_FLAG CCNET_DEBUG_NETIO */ +#include "log.h" + +void +ccnet_peer_packet_prepare (const CcnetPeer *peer, int type, int id) +{ + ccnet_header header; + + header.version = 1; + header.type = type; + header.length = 0; + header.id = htonl (id); + evbuffer_add (peer->packet, &header, sizeof (header)); +} + +void +ccnet_peer_packet_write_string (const CcnetPeer *peer, const char *str) +{ + int len; + + len = strlen(str); + evbuffer_add (peer->packet, str, len); +} + +void +ccnet_peer_packet_finish (const CcnetPeer *peer) +{ + ccnet_header *header; + header = (ccnet_header *) EVBUFFER_DATA(peer->packet); + header->length = htons (EVBUFFER_LENGTH(peer->packet) + - CCNET_PACKET_LENGTH_HEADER); +} + +void +ccnet_peer_packet_send (const CcnetPeer *peer) +{ + int ret = 0; + if (peer->is_local) { + bufferevent_write_buffer (peer->io->bufev, peer->packet); + return; + } + + if (peer->net_state == PEER_CONNECTED) { + if (!peer->encrypt_channel) { + ret = bufferevent_write_buffer (peer->io->bufev, peer->packet); + } else { + ccnet_header enc_header; + char *data = (char *)EVBUFFER_DATA(peer->packet); + uint32_t len = EVBUFFER_LENGTH(peer->packet); + char *enc_data; + int enc_len; + ret = ccnet_encrypt_with_key (&enc_data, &enc_len, data, len, + peer->key, peer->iv); + if (ret < 0) { + ccnet_warning ("[SEND] encryption error for sending packet " + "to peer %s(%.8s) \n", peer->name, peer->id); + evbuffer_drain (peer->packet, EVBUFFER_LENGTH(peer->packet)); + return; + } + + enc_header.version = 1; + enc_header.type = CCNET_MSG_ENCPACKET; + enc_header.length = 0; + enc_header.id = htonl(enc_len); + bufferevent_write (peer->io->bufev, &enc_header, sizeof (enc_header)); + bufferevent_write (peer->io->bufev, enc_data, enc_len); + g_free (enc_data); + evbuffer_drain (peer->packet, EVBUFFER_LENGTH(peer->packet)); + } + if (ret < 0) + ccnet_warning ("[SEND] bufferevent failed to send packet to peer(%.8s) \n", + peer->id); + } else { + ccnet_warning ("Unable to send packet when peer is not connected.\n"); + evbuffer_drain (peer->packet, EVBUFFER_LENGTH(peer->packet)); + } +} + +void +ccnet_peer_packet_finish_send (const CcnetPeer *peer) +{ + ccnet_peer_packet_finish (peer); + ccnet_peer_packet_send (peer); +} + +void +ccnet_peer_send_request (const CcnetPeer *peer, int req_id, const char *req) +{ + if (!peer->is_local) + ccnet_debug ("[network] Send a request: id %d, cmd %s\n", req_id, req); + ccnet_peer_packet_prepare (peer, CCNET_MSG_REQUEST, req_id); + ccnet_peer_packet_write_string (peer, req); + ccnet_peer_packet_finish_send (peer); +} + + +void +ccnet_peer_send_response (const CcnetPeer *peer, int req_id, + const char *code, const char *reason, + const char *content, int clen) +{ + if ( (strlen(code) != 3) || !isdigit(code[0]) || !isdigit(code[1]) + || !isdigit(code[1]) ) { + ccnet_warning ("Bad code number\n"); + return; + } + + g_return_if_fail (clen < 65536); + + ccnet_peer_packet_prepare (peer, CCNET_MSG_RESPONSE, req_id); + + /* code line */ + evbuffer_add (peer->packet, code, 3); + if (reason) { + evbuffer_add (peer->packet, " ", 1); + ccnet_peer_packet_write_string (peer, reason); + } + evbuffer_add (peer->packet, "\n", 1); + + if (content) + evbuffer_add (peer->packet, content, clen); + + ccnet_peer_packet_finish_send (peer); + + if (!peer->is_local) + ccnet_debug ("[SEND] Send a response: id %d code %s %s\n", + req_id, code, reason); +} + +void +ccnet_peer_send_update (const CcnetPeer *peer, int req_id, + const char *code, const char *reason, + const char *content, int clen) +{ + ccnet_peer_packet_prepare (peer, CCNET_MSG_UPDATE, req_id); + + /* code line */ + evbuffer_add (peer->packet, code, 3); + if (reason) { + evbuffer_add (peer->packet, " ", 1); + ccnet_peer_packet_write_string (peer, reason); + } + evbuffer_add (peer->packet, "\n", 1); + + if (content) + evbuffer_add (peer->packet, content, clen); + + ccnet_peer_packet_finish_send (peer); + + if (!peer->is_local) + ccnet_debug ("[SEND] Send an update: id %d code %s %s\n", + req_id, code, reason?reason:"NULL"); +} + + +/* ---------------- Processors ---------------- */ + +#undef DEBUG_FLAG +#define DEBUG_FLAG CCNET_DEBUG_PROCESSOR +#include "log.h" + +void +ccnet_peer_add_processor (CcnetPeer *peer, CcnetProcessor *processor) +{ + if (!peer->is_local) + ccnet_debug ("[Proc] Add %s(%d) to peer %s\n", GET_PNAME(processor), + PRINT_ID(processor->id), peer->name); + g_hash_table_insert (peer->processors, (gpointer)(long)processor->id, processor); + processor->detached = 0; +} + + +void +ccnet_peer_remove_processor (CcnetPeer *peer, CcnetProcessor *processor) +{ + /* ccnet_debug ("[Proc] Remove %s(%d) from peer %s\n", GET_PNAME(processor), */ + /* PRINT_ID(processor->id), peer->name); */ + g_hash_table_remove (peer->processors, (gpointer)(long)processor->id); + processor->detached = 1; +} + + +CcnetProcessor * +ccnet_peer_get_processor (CcnetPeer *peer, unsigned int id) +{ + return g_hash_table_lookup (peer->processors, (gpointer)(long)id); +} + + +static void shutdown_processors (CcnetPeer *peer) +{ + ccnet_proc_factory_shutdown_processors ( + peer->manager->session->proc_factory, peer); +} + + +/* -------- redirect related code -------- */ + +void +ccnet_peer_set_redirect (CcnetPeer *peer, const char *addr, uint16_t port) +{ + g_return_if_fail (peer->redirected == 0); + + peer->redirected = 1; + peer->redirect_addr = g_strdup (addr); + peer->redirect_port = port; +} + +void +ccnet_peer_unset_redirect (CcnetPeer *peer) +{ + peer->redirected = 0; + g_free (peer->redirect_addr); + peer->redirect_port = 0; +} diff --git a/net/common/peer.h b/net/common/peer.h new file mode 100644 index 0000000..4990eb5 --- /dev/null +++ b/net/common/peer.h @@ -0,0 +1,220 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_PEER_H +#define CCNET_PEER_H + + +#include +#include +#include + +#include "processor.h" + + +#define CCNET_TYPE_PEER (ccnet_peer_get_type ()) +#define CCNET_PEER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_PEER, CcnetPeer)) +#define CCNET_IS_PEER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_PEER)) +#define CCNET_PEER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_PEER, CcnetPeerClass)) +#define CCNET_IS_PEER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_PEER)) +#define CCNET_PEER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_PEER, CcnetPeerClass)) + + +enum { + PEER_DOWN, + PEER_CONNECTED +}; + + +typedef struct _CcnetPeer CcnetPeer; +typedef struct _CcnetPeerClass CcnetPeerClass; + + +struct CcnetPacketIO; + +#define NON_RESOLVED_PEERID "0000000000000000000000000000000000000000" + +struct _CcnetUser; + +struct _CcnetPeer +{ + GObject parent_instance; + + /* fields from pubinfo */ + char id[41]; + + RSA *pubkey; + char *session_key; + unsigned char key[32]; + unsigned char iv[32]; + + char *name; /* hostname */ + char *public_addr; + uint16_t public_port; /* port from pubinfo */ + char *service_url; + + /* fields not from pubinfo */ + char *addr_str; /* hold the ip actually used in connection */ + uint16_t port; + + char *redirect_addr; + uint16_t redirect_port; + + char *dns_addr; /* address solved by dns */ + + int net_state; + + GList *role_list; + GList *myrole_list; /* my role on this peer */ + + char *intend_role; /* used in peer resolving */ + + unsigned int is_self : 1; + unsigned int is_local : 1; + unsigned int can_connect : 1; + unsigned int in_local_network : 1; + + unsigned int is_ready : 1; + unsigned int dns_done : 1; + unsigned int need_saving : 1; + + unsigned int want_tobe_relay : 1; /* is the peer used as relay */ + + unsigned int in_shutdown : 1; + unsigned int shutdown_scheduled : 1; + unsigned int in_writecb : 1; + unsigned int in_connection : 1; + unsigned int keepalive_sending : 1; + + unsigned int to_resolve : 1; /* the peer's identity need to be resolved, + * we only have its IP address now. + */ + + unsigned int redirected : 1; + + unsigned int cluster_member : 1; + + unsigned int in_processor_call : 1; + + unsigned int encrypt_channel : 1; + + struct CcnetPacketIO *io; + + + int last_net_state; + + /* for connection management */ + time_t last_down; /* for peer gc in relay */ + int num_fails; + + int reqID; + + struct _CcnetPeerManager *manager; + + struct evbuffer *packet; + + GHashTable *processors; + + GList *write_cbs; + + int last_mult_recv; + + /* statistics */ + time_t last_up; +}; + +struct _CcnetPeerClass +{ + GObjectClass parent_class; +}; + +GType ccnet_peer_get_type (void); + +CcnetPeer* ccnet_peer_from_string (char *content); +void ccnet_peer_update_from_string (CcnetPeer *peer, char *string); +GString * ccnet_peer_to_string (CcnetPeer *peer); + +CcnetPeer* ccnet_peer_new (const char *id); +void ccnet_peer_free (CcnetPeer *peer); + +void ccnet_peer_shutdown (CcnetPeer *peer); + +void ccnet_peer_shutdown_no_delay (CcnetPeer *peer); + +int ccnet_peer_get_request_id (CcnetPeer *peer); + +void ccnet_peer_add_processor (CcnetPeer *peer, + CcnetProcessor *processor); +void ccnet_peer_remove_processor (CcnetPeer *peer, + CcnetProcessor *processor); +CcnetProcessor * + ccnet_peer_get_processor (CcnetPeer *peer, unsigned int id); + +void ccnet_peer_set_net_state (CcnetPeer *peer, int net_state); + +void ccnet_peer_update_address (CcnetPeer *peer, + const char *addr_str, + uint16_t port); + +void ccnet_peer_set_pubkey (CcnetPeer *peer, char *str); + +int ccnet_peer_prepare_channel_encryption (CcnetPeer *peer); + +/* role management */ +void +ccnet_peer_set_roles (CcnetPeer *peer, const char *roles); + +void +ccnet_peer_set_myroles (CcnetPeer *peer, const char *roles); + +void +ccnet_peer_get_roles_str (CcnetPeer *peer, GString *buf); + +void +ccnet_peer_get_myroles_str (CcnetPeer *peer, GString *buf); + +void +ccnet_peer_add_role (CcnetPeer *peer, const char *role); + +void +ccnet_peer_remove_role (CcnetPeer *peer, const char *role); + +gboolean +ccnet_peer_has_role (CcnetPeer *peer, const char *role); + +gboolean +ccnet_peer_has_my_role (CcnetPeer *peer, const char *role); + + + +/* */ +typedef gboolean (*PeerWriteCallback) (CcnetPeer *peer, void *user_data); + +void ccnet_peer_add_write_callback (CcnetPeer *peer, + PeerWriteCallback func, + void *user_data); + +void ccnet_peer_remove_write_callback (CcnetPeer *peer, + PeerWriteCallback func, + void *user_data); + +/* IO */ +void ccnet_peer_send_request (const CcnetPeer *peer, + int req_id, const char *req); +void ccnet_peer_send_response (const CcnetPeer *peer, int req_id, + const char *code, const char *reason, + const char *content, int clen); +void ccnet_peer_send_update (const CcnetPeer *peer, int req_id, + const char *code, const char *reason, + const char *content, int clen); + +/* middle level IO */ + +void ccnet_peer_set_io (CcnetPeer *peer, struct CcnetPacketIO *io); + +void +ccnet_peer_set_redirect (CcnetPeer *peer, const char *addr, uint16_t port); + +void +ccnet_peer_unset_redirect (CcnetPeer *peer); + +#endif diff --git a/net/common/peermgr-message.c b/net/common/peermgr-message.c new file mode 100644 index 0000000..26ded93 --- /dev/null +++ b/net/common/peermgr-message.c @@ -0,0 +1,58 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "session.h" +#include "peer.h" +#include "peer-mgr.h" +#include "utils.h" +#include "peermgr-message.h" + +#define DEBUG_FLAG CCNET_DEBUG_PEER +#include "log.h" + + +/* +Protocol v1: + + PeermgrMessage format: + v\n + \n + [content] + + For example: + v1\n + role-notify\n + MyPeer\n + + */ + + +int +parse_peermgr_message (CcnetMessage *msg, guint16 *version, + char **type, char **body) +{ + char *end, *start; + int v; + + if (!msg->body) + return -1; + + /* version */ + if (msg->body[0] != 'v') + return -1; + + start = msg->body + 1; + if ( !(end = strchr(start, '\n')) ) return -1; + *end = '\0'; + if ((v = atoi(start)) == 0) return -1; + *version = v; + + /* type */ + *type = start = end + 1; + if ( !(end = strchr(start, '\n')) ) return -1; + *end = '\0'; + + *body = start = end + 1; + return 0; +} diff --git a/net/common/peermgr-message.h b/net/common/peermgr-message.h new file mode 100644 index 0000000..776ce37 --- /dev/null +++ b/net/common/peermgr-message.h @@ -0,0 +1,19 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_PEERMGR_MESSAGE_H +#define CCNET_PEERMGR_MESSAGE_H + +#include "message.h" + +#define PEERMGR_VERSION 3 + +#define BIND_QUERY "bind-query" +#define BIND_STATUS "bind-status" +#define ROLE_NOTIFY "role-notify" +#define SERVICE_READY "service-ready" +#define PEER_REDIRECT "redirect" + +int parse_peermgr_message (CcnetMessage *msg, guint16 *version, + char **type, char **body); + +#endif diff --git a/net/common/perm-mgr.c b/net/common/perm-mgr.c new file mode 100644 index 0000000..6370172 --- /dev/null +++ b/net/common/perm-mgr.c @@ -0,0 +1,190 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "peer.h" +#include "session.h" +#include "peer-mgr.h" +#include "perm-mgr.h" + +#define DEBUG_FLAG CCNET_DEBUG_OTHER +#include "log.h" + +/* Give an user and a service, for each role of this user: + 1. find the service group, + 2. use hash table 'role2groups' to find the permitted groups to this role, + 3. check whether the given group in the list of groups. +*/ + +struct _CcnetPermManagerPriv { + GHashTable *serv2group; /* service to group map */ + GHashTable *role2groups; /* role -> list of groups */ + GList *anonymous_groups; /* permitted groups to anonymous user. */ +}; + +struct ServiceGroup { + const char *service; + const char *group; +}; + +/* only service that can be accessed from network peers should list here, + * service accessed only by local peers do not need be list here. */ +struct ServiceGroup service_groups[] = { + { "put-pubinfo", "basic" }, + { "put-user", "basic" }, + { "keepalive2", "basic" }, + { "receive-session-key", "basic" }, + { "receive-skey2", "basic" }, + { "receive-msg", "basic" }, + { "echo", "basic" }, + { "ccnet-rpcserver", "rpc-inner" }, +#ifdef CCNET_SERVER + { "recvlogin", "basic" }, + { "recvlogout", "basic" }, + { "receive-profile", "relay-service" }, +#endif + { NULL, NULL }, +}; + +struct RolePerm { + const char *role; + const char *group; +}; + +typedef struct RolePermList { + GList *list; +} RolePermList; + +struct RolePerm role_perms[] = { + { "MyClient", "seafserv" }, + { "MyClient", "relay-service" }, + { "MyAdmin", "rpc-inner" }, + { "ClusterMember", "seafserv-inner" }, + { "BlockClient", "seablock" }, + { NULL, NULL }, +}; + +CcnetPermManager * +ccnet_perm_manager_new (CcnetSession *session) +{ + CcnetPermManager *mgr = g_new0 (CcnetPermManager, 1); + mgr->priv = g_new0 (CcnetPermManagerPriv, 1); + mgr->session = session; + mgr->priv->serv2group = g_hash_table_new (g_str_hash, g_str_equal); + mgr->priv->role2groups = g_hash_table_new (g_str_hash, g_str_equal); + return mgr; +} + +static void populate_default_items(CcnetPermManager *mgr); + +int +ccnet_perm_manager_prepare (CcnetPermManager *mgr) +{ + populate_default_items (mgr); + return 0; +} + +static void +populate_default_items (CcnetPermManager *mgr) +{ + struct ServiceGroup *sg; + for (sg = service_groups; sg->service; sg++) { + g_hash_table_insert (mgr->priv->serv2group, g_strdup(sg->service), + g_strdup(sg->group)); + } + + struct RolePerm *rp; + for (rp = role_perms; rp->role; rp++) { + RolePermList *list; + list = g_hash_table_lookup (mgr->priv->role2groups, rp->role); + if (!list) { + list = g_new0(RolePermList, 1); + g_hash_table_insert (mgr->priv->role2groups, g_strdup(rp->role), + list); + } + list->list = g_list_prepend (list->list, g_strdup(rp->group)); + } +} + +static inline const char * +get_service_group(CcnetPermManager *mgr, const char *service) +{ + return g_hash_table_lookup (mgr->priv->serv2group, service); +} + +int +check_role_permission(CcnetPermManager *mgr, const char *role, const char *group) +{ + RolePermList *rplist; + GList *ptr; + + rplist = g_hash_table_lookup (mgr->priv->role2groups, role); + if (!rplist) + return PERM_CHECK_ERROR; + + for (ptr = rplist->list; ptr; ptr = ptr->next) { + if (strcmp(ptr->data, group) == 0) + return PERM_CHECK_OK; + } + return PERM_CHECK_ERROR; +} + +/* for unit test */ +int +ccnet_perm_manager_check_role_permission(CcnetPermManager *mgr, + const char *role, const char *group) +{ + return check_role_permission (mgr, role, group); +} + +int +ccnet_perm_manager_check_permission (CcnetPermManager *mgr, + CcnetPeer *peer, + const char *req, + int req_id, + int argc, char **argv) +{ + const char *group = get_service_group (mgr, req); + if (!group) + return PERM_CHECK_NOSERVICE; + + if (g_strcmp0(group, "basic") == 0) + return PERM_CHECK_OK; + + if (peer->is_local) + return PERM_CHECK_OK; + + if (g_strcmp0(group, "inner") == 0) + return PERM_CHECK_ERROR; + + if (g_strcmp0 (group, "self") == 0) { + if (g_strcmp0 (peer->id, mgr->session->base.id) == 0) + /* myself user */ + return PERM_CHECK_OK; + else + return PERM_CHECK_ERROR; + } + + GList *ptr; + for (ptr = peer->role_list; ptr; ptr = ptr->next) { + if (check_role_permission(mgr, ptr->data, group) == PERM_CHECK_OK) + return PERM_CHECK_OK; + } + + return PERM_CHECK_ERROR; +} + +int +ccnet_perm_manager_register_service (CcnetPermManager *mgr, + const char *service, + const char *group, + CcnetPeer *peer) +{ + if (g_hash_table_lookup (mgr->priv->serv2group, service)) + return -1; + + ccnet_debug ("[perm-mgr] register service %s %s\n", service, group); + g_hash_table_insert (mgr->priv->serv2group, g_strdup(service), + g_strdup(group)); + return 0; +} diff --git a/net/common/perm-mgr.h b/net/common/perm-mgr.h new file mode 100644 index 0000000..78b7b20 --- /dev/null +++ b/net/common/perm-mgr.h @@ -0,0 +1,48 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef SEAF_PERM_MGR_H +#define SEAF_PERM_MGR_H + +#include + + +enum { + PERM_CHECK_ERROR = -1, + PERM_CHECK_OK = 0, + PERM_CHECK_DELAY = 1, + PERM_CHECK_NOSERVICE = 2, +}; + +typedef struct _CcnetPermManager CcnetPermManager; +typedef struct _CcnetPermManagerPriv CcnetPermManagerPriv; + +struct _CcnetPermManager { + CcnetSession *session; + + CcnetPermManagerPriv *priv; +}; + +CcnetPermManager* +ccnet_perm_manager_new (CcnetSession *session); + +int +ccnet_perm_manager_prepare (CcnetPermManager *mgr); + +int +ccnet_perm_manager_check_permission (CcnetPermManager *mgr, + CcnetPeer *peer, + const char *req, + int req_id, + int argc, char **argv); + +int +ccnet_perm_manager_check_role_permission(CcnetPermManager *mgr, + const char *role, + const char *group); + +int +ccnet_perm_manager_register_service (CcnetPermManager *mgr, + const char *svc_name, + const char *group, + CcnetPeer *peer); +#endif diff --git a/net/common/proc-factory.c b/net/common/proc-factory.c new file mode 100644 index 0000000..b324b42 --- /dev/null +++ b/net/common/proc-factory.c @@ -0,0 +1,333 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include + +#include "peer.h" +#include "session.h" + +#include "processor.h" +#include "proc-factory.h" +#include "peer-mgr.h" +#include "timer.h" +#include "processors/keepalive2-proc.h" +#include "processors/rcvcmd-proc.h" +#include "processors/service-proxy-proc.h" +#include "processors/service-stub-proc.h" +#include "processors/sendsessionkey-proc.h" +#include "processors/recvsessionkey-proc.h" +#include "processors/sendsessionkey-v2-proc.h" +#include "processors/recvsessionkey-v2-proc.h" + + +#define DEBUG_FLAG CCNET_DEBUG_PROCESSOR +#include "log.h" +#include "utils.h" + + +/* Note, timeout here must be larger than the timeout of keepalive-proc + * + * Here, we only handle problems which happen when tcp connection is ok but + * processors are dead or not created by peer. + * + * + */ + +/* The timeout of keepalive-proc is 180s now. */ +#define DEFAULT_NO_PACKET_TIMEOUT 10 /* 10 seconds */ +#define KEEPALIVE_PULSE 5 * 1000 /* 5 seconds */ +#define CONNECTION_TIMEOUT 182 +#define MAX_PROCS_KEEPALIVE 5 /* we check 5 proc for each peer at most */ + +typedef struct { + GHashTable *proc_type_table; +} CcnetProcFactoryPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_PROC_FACTORY, CcnetProcFactoryPriv)) + +G_DEFINE_TYPE (CcnetProcFactory, ccnet_proc_factory, G_TYPE_OBJECT); + +static void +ccnet_proc_factory_class_init (CcnetProcFactoryClass *klass) +{ + g_type_class_add_private (klass, sizeof (CcnetProcFactoryPriv)); +} + +static void +ccnet_proc_factory_init (CcnetProcFactory *factory) +{ + CcnetProcFactoryPriv *priv = GET_PRIV (factory); + + priv->proc_type_table = g_hash_table_new_full ( + g_str_hash, g_str_equal, g_free, NULL); +} + +void +ccnet_proc_factory_register_processor (CcnetProcFactory *factory, + const char *serv_name, + GType type) +{ + CcnetProcFactoryPriv *priv = GET_PRIV (factory); + + CcnetProcessorClass *proc_class = + (CcnetProcessorClass *)g_type_class_ref(type); + g_type_class_unref (proc_class); + + g_hash_table_insert (priv->proc_type_table, g_strdup (serv_name), + (gpointer) type); +} + + +GType ccnet_getpubinfo_proc_get_type (); +GType ccnet_putpubinfo_proc_get_type (); + +GType ccnet_sendmsg_proc_get_type (); +GType ccnet_rcvmsg_proc_get_type (); + +GType ccnet_rcvcmd_proc_get_type (); +GType ccnet_getperm_proc_get_type (); + +GType ccnet_keepalive2_proc_get_type (); + +GType ccnet_mqserver_proc_get_type (); + +GType ccnet_service_proxy_proc_get_type (); +GType ccnet_service_stub_proc_get_type (); + +GType ccnet_sync_relay_proc_get_type (); +GType ccnet_sync_relay_slave_proc_get_type (); + +GType ccnet_rpcserver_proc_get_type (); +GType ccnet_echo_proc_get_type (); + +CcnetProcFactory * +ccnet_proc_factory_new (CcnetSession *session) +{ + CcnetProcFactory *factory; + + factory = g_object_new (CCNET_TYPE_PROC_FACTORY, NULL); + factory->session = session; + factory->no_packet_timeout = DEFAULT_NO_PACKET_TIMEOUT; + factory->procs = NULL; + + /* register fundamental processors */ + /* FIXME: These processor types shall be regitered by managers */ + ccnet_proc_factory_register_processor (factory, "get-pubinfo", + ccnet_getpubinfo_proc_get_type ()); + ccnet_proc_factory_register_processor (factory, "put-pubinfo", + ccnet_putpubinfo_proc_get_type ()); + + ccnet_proc_factory_register_processor (factory, "send-msg", + ccnet_sendmsg_proc_get_type ()); + ccnet_proc_factory_register_processor (factory, "receive-msg", + ccnet_rcvmsg_proc_get_type ()); + + ccnet_proc_factory_register_processor (factory, "receive-cmd", + ccnet_rcvcmd_proc_get_type ()); + /* ccnet_proc_factory_register_processor (factory, "receive-event", */ + /* ccnet_rcvevent_proc_get_type ()); */ + + ccnet_proc_factory_register_processor (factory, "keepalive2", + ccnet_keepalive2_proc_get_type ()); + + ccnet_proc_factory_register_processor (factory, "send-session-key", + ccnet_sendsessionkey_proc_get_type()); + + ccnet_proc_factory_register_processor (factory, "receive-session-key", + ccnet_recvsessionkey_proc_get_type ()); + + ccnet_proc_factory_register_processor (factory, "send-skey2", + ccnet_sendskey2_proc_get_type()); + ccnet_proc_factory_register_processor (factory, "receive-skey2", + ccnet_recvskey2_proc_get_type ()); + + ccnet_proc_factory_register_processor (factory, "mq-server", + ccnet_mqserver_proc_get_type ()); + + ccnet_proc_factory_register_processor (factory, "service-proxy", + ccnet_service_proxy_proc_get_type()); + + ccnet_proc_factory_register_processor (factory, "service-stub", + ccnet_service_stub_proc_get_type()); + + ccnet_proc_factory_register_processor (factory, "ccnet-rpcserver", + ccnet_rpcserver_proc_get_type()); + + + +/* + if (session->is_relay) { + ccnet_proc_factory_register_processor (factory, "sync-relay-slave", + ccnet_sync_relay_slave_proc_get_type()); + } else { + ccnet_proc_factory_register_processor (factory, "sync-relay", + ccnet_sync_relay_proc_get_type()); + } +*/ + + ccnet_proc_factory_register_processor (factory, "echo", + ccnet_echo_proc_get_type ()); + + return factory; +} + +void +ccnet_proc_factory_start (CcnetProcFactory *factory) +{ + /* factory->keepalive_timer = ccnet_timer_new ( */ + /* (TimerCB) keepalive_pulse, factory, KEEPALIVE_PULSE); */ +} + +static GType +ccnet_proc_factory_get_proc_type (CcnetProcFactory *factory, + const char *serv_name) +{ + CcnetProcFactoryPriv *priv = GET_PRIV (factory); + + return (GType) g_hash_table_lookup (priv->proc_type_table, serv_name); +} + +static inline CcnetProcessor * +create_processor_common (CcnetProcFactory *factory, + const char *serv_name, + CcnetPeer *peer, + int req_id) +{ + GType type; + CcnetProcessor *processor; + + type = ccnet_proc_factory_get_proc_type (factory, serv_name); + if (type == 0) { + return NULL; + } + + processor = g_object_new (type, NULL); + processor->peer = peer; + g_object_ref (peer); + processor->session = factory->session; + processor->id = req_id; + /* Set the real processor name. + * This may be different from the processor class name. + */ + processor->name = g_strdup(serv_name); + + if (!peer->is_local) + ccnet_debug ("Create processor %s(%d) %s\n", GET_PNAME(processor), + PRINT_ID(processor->id), processor->name); + ccnet_peer_add_processor (processor->peer, processor); + + factory->procs_alive_cnt++; + + return processor; +} + +CcnetProcessor * +ccnet_proc_factory_create_slave_processor (CcnetProcFactory *factory, + const char *serv_name, + CcnetPeer *peer, + int req_id) +{ + + return create_processor_common(factory, serv_name, + peer, SLAVE_ID (req_id)); +} + +CcnetProcessor * +ccnet_proc_factory_create_master_processor (CcnetProcFactory *factory, + const char *serv_name, + CcnetPeer *peer) +{ + return create_processor_common ( + factory, serv_name, + peer, MASTER_ID(ccnet_peer_get_request_id (peer)) ); +} + +static void inline +recycle (CcnetProcFactory *factory, CcnetProcessor *processor) +{ + factory->procs_alive_cnt--; + +#ifdef DEBUG_PROC + if (strcmp(GET_PNAME(processor), "rpcserver-proc") != 0) { + /* ignore rpcserver-proc */ + + CcnetProc *proc = ccnet_proc_new(); + g_object_set (proc, "name", GET_PNAME(processor), + "peer-name", processor->peer->name, + "ctime", (int) processor->start_time, + "dtime", (int) time(NULL), NULL); + factory->procs = g_list_prepend (factory->procs, proc); + } +#endif + + /* TODO: implement processor pool */ + g_object_unref (processor); +} + +void +ccnet_proc_factory_recycle (CcnetProcFactory *factory, + CcnetProcessor *processor) +{ + recycle (factory, processor); +} + +static void +shutdown_processor (CcnetProcessor *processor, + char *code, char *code_msg) +{ + /* Send an error message to shutdown the processor. + * If it's a proxy or stub proc, it'll first relay the message. + */ + if (!IS_SLAVE (processor)) { + ccnet_processor_handle_response (processor, code, code_msg, + NULL, 0); + } else { + ccnet_processor_handle_update (processor, code, code_msg, + NULL, 0); + } +} + +void +ccnet_proc_factory_shutdown_processors (CcnetProcFactory *factory, + CcnetPeer *peer) +{ + GList *list, *ptr; + CcnetProcessor *processor; + char *code = g_strdup (SC_NETDOWN); + char *code_msg = g_strdup (SS_NETDOWN); + + list = g_hash_table_get_values (peer->processors); + for (ptr = list; ptr; ptr = ptr->next) { + processor = CCNET_PROCESSOR (ptr->data); + processor->detached = TRUE; + shutdown_processor (processor, code, code_msg); + } + g_hash_table_remove_all (peer->processors); + g_list_free (list); + + g_free (code); + g_free (code_msg); +} + +void +ccnet_proc_factory_set_keepalive_timeout (CcnetProcFactory *factory, + int timeout) +{ + factory->no_packet_timeout = timeout; +} + +/* Don't send keepalive or reclaim inactive processors. */ + +#if 0 + +static gint +compare_procs (gconstpointer a, gconstpointer b) +{ + const CcnetProcessor *proc_a = a, *proc_b = b; + + return (proc_a->t_keepalive_sent - proc_b->t_keepalive_sent); +} + +#endif /* 0 */ diff --git a/net/common/proc-factory.h b/net/common/proc-factory.h new file mode 100644 index 0000000..45bcc2e --- /dev/null +++ b/net/common/proc-factory.h @@ -0,0 +1,68 @@ +#ifndef CCNET_PROC_FACTORY_H +#define CCNET_PROC_FACTORY_H + + +#include + +#include "processor.h" +#include "ccnet-object.h" + +#define CCNET_TYPE_PROC_FACTORY (ccnet_proc_factory_get_type ()) +#define CCNET_PROC_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_PROC_FACTORY, CcnetProcFactory)) +#define CCNET_IS_PROC_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_PROC_FACTORY)) +#define CCNET_PROC_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_PROC_FACTORY, CcnetProcFactoryClass)) +#define CCNET_IS_PROC_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_PROC_FACTORY)) +#define CCNET_PROC_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_PROC_FACTORY, CcnetProcFactoryClass)) + +typedef struct _CcnetProcFactory CcnetProcFactory; +typedef struct _CcnetProcFactoryClass CcnetProcFactoryClass; + +struct _CcnetProcFactory +{ + GObject parent_instance; + + /* protected */ + struct CcnetSession *session; + + int procs_alive_cnt; /*number of processors alive*/ + + GList *procs; /* TODO: need to recyle the space + * when it grows verylarge */ + + /* do keepalive if not receiving packet in `no_packet_timeout`, + * default is 30 seconds */ + int no_packet_timeout; +}; + +struct _CcnetProcFactoryClass +{ + GObjectClass parent_class; +}; + +GType ccnet_proc_factory_get_type (void); + +CcnetProcFactory *ccnet_proc_factory_new (CcnetSession *session); +void ccnet_proc_factory_start (CcnetProcFactory *factory); + +void ccnet_proc_factory_register_processor (CcnetProcFactory *facotry, + const char *serv_name, + GType proc_type); + +void ccnet_proc_factory_recycle(CcnetProcFactory *factory, + CcnetProcessor *processor); + +void ccnet_proc_factory_shutdown_processors ( + CcnetProcFactory *factory, CcnetPeer *peer); + + +CcnetProcessor *ccnet_proc_factory_create_master_processor ( + CcnetProcFactory *factory, const char *serv_name, CcnetPeer *peer); + +CcnetProcessor *ccnet_proc_factory_create_slave_processor ( + CcnetProcFactory *factory, const char *serv_name, + CcnetPeer *peer, int req_id); + +void ccnet_proc_factory_set_keepalive_timeout (CcnetProcFactory *factory, + int timeout); + +#endif diff --git a/net/common/processor.c b/net/common/processor.c new file mode 100644 index 0000000..463e406 --- /dev/null +++ b/net/common/processor.c @@ -0,0 +1,484 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + + +#include "common.h" + +#include "timer.h" +#include "peer.h" +#include "processor.h" +#include "session.h" +#include "connect-mgr.h" +#include "proc-factory.h" +#include "utils.h" + +#ifdef CCNET_SERVER +#include "server-session.h" +#include "job-mgr.h" +#endif + +#include "processors/keepalive2-proc.h" +#include "processors/service-proxy-proc.h" +#include "processors/service-stub-proc.h" + +#define DEBUG_FLAG CCNET_DEBUG_PROCESSOR +#include "log.h" + +enum { + DONE_SIG, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (CcnetProcessor, ccnet_processor, G_TYPE_OBJECT); + +static void default_shutdown (CcnetProcessor *processor); +static void default_release_resource (CcnetProcessor *processor); + +static void +ccnet_processor_class_init (CcnetProcessorClass *klass) +{ + /* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */ + + klass->start = NULL; + klass->handle_update = NULL; + klass->handle_response = NULL; + klass->shutdown = default_shutdown; + klass->release_resource = default_release_resource; + + signals[DONE_SIG] = + g_signal_new ("done", CCNET_TYPE_PROCESSOR, + G_SIGNAL_RUN_LAST, + 0, /* no class singal handler */ + NULL, NULL, /* no accumulator */ + g_cclosure_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, G_TYPE_BOOLEAN); +} + +static void +ccnet_processor_init (CcnetProcessor *processor) +{ + +} + +int ccnet_processor_start (CcnetProcessor *processor, int argc, char **argv) +{ + /* set this value to now even if this is a master processor and + has not received any packet yet for simplifying the keepalive + logic. */ + + time_t now = time(NULL); + processor->start_time = now; + if (IS_SLAVE(processor)) + processor->t_packet_recv = now; + else + processor->t_packet_recv = 0; + + processor->failure = PROC_NOTSET; + if (processor->peer->net_state != PEER_CONNECTED) { + if (IS_SLAVE(processor)) { + ccnet_processor_send_response (processor, SC_NETDOWN, SS_NETDOWN, + NULL, 0); + } + processor->failure = PROC_NETDOWN; + ccnet_processor_done (processor, FALSE); + return -1; + } + + return CCNET_PROCESSOR_GET_CLASS (processor)->start ( + processor, argc, argv); +} + +int ccnet_processor_startl (CcnetProcessor *processor, ...) +{ + va_list ap; + int argc = 0; + char **argv = g_malloc (sizeof(char *) * 10); + char *arg; + int max = 10; + int ret; + + va_start (ap, processor); + arg = va_arg (ap, char *); + while (arg) { + if (argc >= max) { + max *= 2; + argv = g_realloc (argv, sizeof(char *) * max); + } + argv[argc++] = arg; + + arg = va_arg (ap, char *); + } + va_end (ap); + + ret = ccnet_processor_start (processor, argc, argv); + g_free (argv); + + return ret; +} + +static void default_shutdown (CcnetProcessor *processor) +{ + processor->err_code = ERR_INTR; +} + +static void default_release_resource(CcnetProcessor *processor) +{ + if (processor->retry_timer) + ccnet_timer_free (&processor->retry_timer); + + g_free (processor->name); + if (processor->peer) { + g_object_unref (processor->peer); + processor->peer = NULL; + } +} + +/* should be called before recycle */ +void +ccnet_processor_release_resource(CcnetProcessor *processor) +{ + CCNET_PROCESSOR_GET_CLASS (processor)->release_resource(processor); +} + +/* + * processor->detached is set in two places: + * 1. ccnet_peer_remove_process(), which is called when one processor is done; + * 2. ccnet_proc_factory_shutdown_processors(), which is called when peer shutdown. + * + * There are two shutdown/done situation: + * 1. processor thread is not running; + * 2. processor thread was running, now it's a delayed shutdown. + * + * When ccnet_processor_done() is called while worker thread is running, + * the current state is saved in processor->detached, processor->was_success. + * After the thread is done, ccnet_processor_done() will be called again + * with the same state. + */ + +void +ccnet_processor_done (CcnetProcessor *processor, gboolean success) +{ + if (processor->thread_running) { + processor->delay_shutdown = TRUE; + processor->was_success = success; + return; + } + + if (processor->state == STATE_IN_SHUTDOWN) { + return; + } + + processor->state = STATE_IN_SHUTDOWN; + if (processor->failure == PROC_NOTSET) + processor->failure = PROC_DONE; + if (!processor->peer->is_local) + ccnet_debug ("Processsor %s(%d) done %d\n", GET_PNAME(processor), + PRINT_ID(processor->id), success); + + if (!processor->detached && success) + { + if (!IS_SLAVE (processor)) { + ccnet_processor_send_update (processor, SC_PROC_DONE, SS_PROC_DONE, + NULL, 0); + } + } + + g_signal_emit (processor, signals[DONE_SIG], 0, success); + + if (!processor->detached) { + ccnet_peer_remove_processor (processor->peer, processor); + } + + ccnet_processor_release_resource (processor); + + ccnet_proc_factory_recycle (processor->session->proc_factory, processor); +} + + +void +ccnet_processor_error (CcnetProcessor *processor, + const char *error_code, + const char *error_string) +{ + ccnet_processor_send_response ( + processor, error_code, error_string, NULL, 0); + ccnet_processor_done (processor, FALSE); +} + +static void ccnet_processor_keep_alive_response (CcnetProcessor *processor); + +void ccnet_processor_handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + if (code[0] == '5' || code[0] == '4') { + ccnet_debug ("[Proc] Shutdown processor %s(%d) for bad update: %s %s\n", + GET_PNAME(processor), PRINT_ID(processor->id), + code, code_msg); + + /* Proxy proc should relay the message before it shuts down. */ + if (CCNET_IS_SERVICE_PROXY_PROC(processor) || + CCNET_IS_SERVICE_STUB_PROC(processor)) { + CCNET_PROCESSOR_GET_CLASS (processor)->handle_update ( + processor, code, code_msg, content, clen); + } + + if (memcmp(code, SC_UNKNOWN_SERVICE, 3) == 0) + processor->failure = PROC_NO_SERVICE; + else if (memcmp(code, SC_PERM_ERR, 3) == 0) + processor->failure = PROC_PERM_ERR; + else if (memcmp(code, SC_CON_TIMEOUT, 3) == 0) + processor->failure = PROC_CON_TIMEOUT; + else if (memcmp(code, SC_KEEPALIVE_TIMEOUT, 3) == 0) + processor->failure = PROC_TIMEOUT; + else if (memcmp(code, SC_NETDOWN, 3) == 0) + processor->failure = PROC_NETDOWN; + else + processor->failure = PROC_BAD_RESP; + + ccnet_processor_done (processor, FALSE); + + return; + } + + processor->t_packet_recv = time(NULL); + + if (memcmp (code, SC_PROC_KEEPALIVE, 3) == 0) { + ccnet_processor_keep_alive_response (processor); + } else if (memcmp (code, SC_PROC_ALIVE, 3) == 0) { + /* ccnet_debug ("[Proc] received alive update (%d)\n", + PRINT_ID(processor->id)); + */ + /* do nothing */ + } else if (memcmp (code, SC_PROC_DEAD, 3) == 0) { + ccnet_debug ("[Proc] Shutdown processor %s(%d) when remote processor dies\n", + GET_PNAME(processor), PRINT_ID(processor->id)); + + if (CCNET_IS_SERVICE_PROXY_PROC(processor) || + CCNET_IS_SERVICE_STUB_PROC(processor)) { + CCNET_PROCESSOR_GET_CLASS (processor)->handle_update ( + processor, code, code_msg, content, clen); + } + + processor->failure = PROC_REMOTE_DEAD; + ccnet_processor_done (processor, FALSE); + } else if (memcmp (code, SC_PROC_DONE, 3) == 0) { + ccnet_debug ("[Proc] Shutdown processor %s(%d) when master done\n", + GET_PNAME(processor), PRINT_ID(processor->id)); + + if (CCNET_IS_SERVICE_PROXY_PROC(processor) || + CCNET_IS_SERVICE_STUB_PROC(processor)) { + CCNET_PROCESSOR_GET_CLASS (processor)->handle_update ( + processor, code, code_msg, content, clen); + } + + ccnet_processor_done (processor, TRUE); + } else + CCNET_PROCESSOR_GET_CLASS (processor)->handle_update (processor, + code, code_msg, + content, clen); +} + +void ccnet_processor_handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + if ((code[0] == '5' || code[0] == '4') && + !CCNET_IS_KEEPALIVE2_PROC(processor)) + { + ccnet_debug ("[Proc] peer %.10s, Shutdown processor %s(%d) for bad response: %s %s\n", + processor->peer->id, GET_PNAME(processor), PRINT_ID(processor->id), + code, code_msg); + + /* Stub proc should relay the message before it shuts down. */ + if (CCNET_IS_SERVICE_PROXY_PROC(processor) || + CCNET_IS_SERVICE_STUB_PROC(processor)) { + CCNET_PROCESSOR_GET_CLASS (processor)->handle_response ( + processor, code, code_msg, content, clen); + } + + if (memcmp(code, SC_UNKNOWN_SERVICE, 3) == 0) + processor->failure = PROC_NO_SERVICE; + else if (memcmp(code, SC_PERM_ERR, 3) == 0) + processor->failure = PROC_PERM_ERR; + else if (memcmp(code, SC_CON_TIMEOUT, 3) == 0) + processor->failure = PROC_CON_TIMEOUT; + else if (memcmp(code, SC_KEEPALIVE_TIMEOUT, 3) == 0) + processor->failure = PROC_TIMEOUT; + else if (memcmp(code, SC_NETDOWN, 3) == 0) + processor->failure = PROC_NETDOWN; + else + processor->failure = PROC_BAD_RESP; + + ccnet_processor_done (processor, FALSE); + + return; + } + + processor->t_packet_recv = time(NULL); + + if (memcmp (code, SC_PROC_KEEPALIVE, 3) == 0) { + ccnet_processor_keep_alive_response (processor); + } else if (memcmp (code, SC_PROC_ALIVE, 3) == 0) { + /* do nothing */ + } else if (memcmp (code, SC_PROC_DEAD, 3) == 0) { + ccnet_debug ("[Proc] Shutdown processor %s(%d) when remote processor dies\n", + GET_PNAME(processor), PRINT_ID(processor->id)); + + if (CCNET_IS_SERVICE_PROXY_PROC(processor) || + CCNET_IS_SERVICE_STUB_PROC(processor)) { + CCNET_PROCESSOR_GET_CLASS (processor)->handle_response ( + processor, code, code_msg, content, clen); + } + + processor->failure = PROC_REMOTE_DEAD; + ccnet_processor_done (processor, FALSE); + } else + CCNET_PROCESSOR_GET_CLASS (processor)->handle_response (processor, + code, code_msg, + content, clen); +} + +void ccnet_processor_handle_sigchld (CcnetProcessor *processor, int status) +{ + CCNET_PROCESSOR_GET_CLASS (processor)->handle_sigchld (processor, + status); +} + +void +ccnet_processor_send_request (CcnetProcessor *processor, + const char *request) +{ + ccnet_peer_send_request (processor->peer, REQUEST_ID (processor->id), + request); +} + +void +ccnet_processor_send_request_l (CcnetProcessor *processor, ...) +{ + va_list ap; + GString *buf = g_string_new(NULL); + char *arg; + + va_start (ap, processor); + arg = va_arg (ap, char *); + while (arg) { + g_string_append (buf, arg); + arg = va_arg (ap, char *); + } + va_end (ap); + + ccnet_peer_send_request (processor->peer, + REQUEST_ID (processor->id), buf->str); + g_string_free (buf, TRUE); +} + + +void +ccnet_processor_send_update (CcnetProcessor *processor, + const char *code, + const char *code_msg, + const char *content, int clen) +{ + ccnet_peer_send_update (processor->peer, UPDATE_ID(processor->id), + code, code_msg, content, clen); +} + +void +ccnet_processor_send_error_update (CcnetProcessor *processor, + const char *code, + const char *code_msg) +{ + ccnet_peer_send_update (processor->peer, UPDATE_ID(processor->id), + code, code_msg, NULL, 0); +} + +void +ccnet_processor_send_response (CcnetProcessor *processor, + const char *code, + const char *code_msg, + const char *content, int clen) +{ + ccnet_peer_send_response (processor->peer, RESPONSE_ID (processor->id), + code, code_msg, content, clen); +} + +void ccnet_processor_keep_alive (CcnetProcessor *processor) +{ + if (IS_SLAVE (processor)) + ccnet_processor_send_response (processor, SC_PROC_KEEPALIVE, + SS_PROC_KEEPALIVE, NULL, 0); + else + ccnet_processor_send_update (processor, SC_PROC_KEEPALIVE, + SS_PROC_KEEPALIVE, NULL, 0); + processor->t_keepalive_sent = time (NULL); +} + +static void ccnet_processor_keep_alive_response (CcnetProcessor *processor) +{ + if (IS_SLAVE (processor)) + ccnet_processor_send_response (processor, SC_PROC_ALIVE, + SS_PROC_ALIVE, NULL, 0); + else + ccnet_processor_send_update (processor, SC_PROC_ALIVE, + SS_PROC_ALIVE, NULL, 0); +} + +#ifdef CCNET_SERVER + +typedef struct ProcThreadData { + CcnetProcessor *proc; + ProcThreadFunc func; + void *data; + ProcThreadDoneFunc done_func; + void *result; +} ProcThreadData; + +static void +processor_thread_done (void *vdata) +{ + ProcThreadData *tdata = vdata; + + tdata->proc->thread_running = FALSE; + + if (tdata->proc->delay_shutdown) + ccnet_processor_done (tdata->proc, tdata->proc->was_success); + else + tdata->done_func (tdata->result); + + g_free (tdata); +} + +static void * +processor_thread_func_wrapper (void *vdata) +{ + ProcThreadData *tdata = vdata; + tdata->result = tdata->func (tdata->data); + return vdata; +} + +int +ccnet_processor_thread_create (CcnetProcessor *processor, + CcnetJobManager *job_mgr, + ProcThreadFunc func, + ProcThreadDoneFunc done_func, + void *data) +{ + ProcThreadData *tdata; + + tdata = g_new(ProcThreadData, 1); + tdata->proc = processor; + tdata->func = func; + tdata->done_func = done_func; + tdata->data = data; + + ccnet_job_manager_schedule_job (job_mgr ? job_mgr : processor->session->job_mgr, + processor_thread_func_wrapper, + processor_thread_done, + tdata); + processor->thread_running = TRUE; + return 0; +} + +#endif /* CCNET_SERVER */ diff --git a/net/common/processor.h b/net/common/processor.h new file mode 100644 index 0000000..4477fdc --- /dev/null +++ b/net/common/processor.h @@ -0,0 +1,200 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_PROCESSOR_H +#define CCNET_PROCESSOR_H + +#include +#include + +#include + +struct CcnetSession; +struct _CcnetPeer; +struct CcnetTimer; + +#define CCNET_TYPE_PROCESSOR (ccnet_processor_get_type ()) +#define CCNET_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_PROCESSOR, CcnetProcessor)) +#define CCNET_IS_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_PROCESSOR)) +#define CCNET_PROCESSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_PROCESSOR, CcnetProcessorClass)) +#define CCNET_IS_PROCESSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_PROCESSOR)) +#define CCNET_PROCESSOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_PROCESSOR, CcnetProcessorClass)) + +typedef struct _CcnetProcessor CcnetProcessor; +typedef struct _CcnetProcessorClass CcnetProcessorClass; + + +/* err_code values */ +enum { + ERR_NONE = 0, + ERR_INTR, + ERR_ARG, + ERR_MAX, +}; + +struct _CcnetProcessor { + GObject parent_instance; + + struct _CcnetPeer *peer; + struct CcnetSession *session; + + char *name; + + /* highest bit = 0, master; highest bit = 1, slave */ + unsigned int id; + + int state; + int failure; + + int mode; + + struct CcnetTimer *retry_timer; + int num_retry; + + int err_code; + + time_t start_time; + + /* Set to 1 if removed from peer->processors */ + unsigned int detached : 1; + + /* last time when a packet received */ + time_t t_packet_recv; + time_t t_keepalive_sent; + + gboolean thread_running; + + /* If proc is shut down when a thread is running, delay it until + * the thread is done. The current state is saved to was_success. + * The thread done callback checks delay_shutdown + * attribute and if this is TRUE, the callback + * calls processor_done(processor, was_success). + */ + gboolean delay_shutdown; + gboolean was_success; +}; + + +enum { + STATE_IN_SHUTDOWN = 1 << 8, + STATE_RECYCLED, +}; + +enum { + PROC_NOTSET, + PROC_DONE, + PROC_REMOTE_DEAD, + PROC_NO_SERVICE, + PROC_PERM_ERR, + PROC_BAD_RESP, /* code[0] =='5' || '4' */ + PROC_NETDOWN, + PROC_CON_TIMEOUT, + PROC_TIMEOUT, +}; + +#define SLAVE_MASK 0x80000000 +#define REQUEST_ID_MASK 0x7fffffff +#define REQUEST_ID(processor_id) ((processor_id) & REQUEST_ID_MASK) +#define UPDATE_ID(processor_id) ((processor_id) & REQUEST_ID_MASK) +#define RESPONSE_ID(processor_id) ((processor_id) & REQUEST_ID_MASK) +#define SLAVE_ID(request_id) ((request_id) | SLAVE_MASK) +#define MASTER_ID(request_id) (request_id) +#define PRINT_ID(processor_id) ((processor_id) & SLAVE_MASK) ? \ + - REQUEST_ID(processor_id) : REQUEST_ID(processor_id) +#define IS_SLAVE(processor) ((processor)->id & SLAVE_MASK) +#define GET_PNAME(processor) CCNET_PROCESSOR_GET_CLASS(processor)->name + +struct _CcnetProcessorClass { + GObjectClass parent_class; + + char *name; + + /* pure virtual function */ + int (*start) (CcnetProcessor *processor, + int argc, char **argv); + void (*handle_update) (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + void (*handle_response) (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + + void (*handle_sigchld) (CcnetProcessor *processor, + int status); + + void (*shutdown) (CcnetProcessor *processor); + + void (*release_resource) (CcnetProcessor *processor); + +}; + +GType ccnet_processor_get_type (); + +int ccnet_processor_start (CcnetProcessor *processor, + int argc, char **argv); + +int ccnet_processor_startl + (CcnetProcessor *processor, ...) G_GNUC_NULL_TERMINATED; + +void ccnet_processor_done (CcnetProcessor *processor, gboolean success); + +void ccnet_processor_handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +void ccnet_processor_handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +void ccnet_processor_handle_sigchld (CcnetProcessor *processor, + int status); + +void ccnet_processor_error (CcnetProcessor *processor, + const char *error_code, + const char *error_string); + +void ccnet_processor_send_request (CcnetProcessor *processor, + const char *request); + +void ccnet_processor_send_request_l (CcnetProcessor *processor, + ...) G_GNUC_NULL_TERMINATED; + + +void ccnet_processor_send_update(CcnetProcessor *processor, + const char *code, + const char *code_msg, + const char *content, int clen); + +void ccnet_processor_send_error_update (CcnetProcessor *processor, + const char *code, + const char *code_msg); + +void ccnet_processor_send_response(CcnetProcessor *processor, + const char *code, + const char *code_msg, + const char *content, int clen); + +void ccnet_processor_keep_alive (CcnetProcessor *processor); + +/* + The thread func should return the result back by + return (void *)result; + The result will be passed to ProcThreadDoneFunc. + In the done func, the caller should check whether processor->delay_shutdown + is TRUE. If it is, you should call processor_done(). + */ +typedef void* (*ProcThreadFunc)(void *data); +typedef void (*ProcThreadDoneFunc)(void *result); + +struct _CcnetJobManager; + +/* + * @job_mgr: the thread pool to create the worker thread. + * If it's NULL, processor->session->job_mgr will be used. + */ +int ccnet_processor_thread_create (CcnetProcessor *processor, + struct _CcnetJobManager *job_mgr, + ProcThreadFunc func, + ProcThreadDoneFunc done_func, + void *data); + +#endif diff --git a/net/common/processors/echo-proc.c b/net/common/processors/echo-proc.c new file mode 100644 index 0000000..4c04b4f --- /dev/null +++ b/net/common/processors/echo-proc.c @@ -0,0 +1,96 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "peer.h" +#include "session.h" + +#include "echo-proc.h" + +#define DEBUG_FLAG CCNET_DEBUG_PROCESSOR +#include "log.h" + + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_ECHO_PROC, CcnetEchoProcPriv)) + +G_DEFINE_TYPE (CcnetEchoProc, ccnet_echo_proc, CCNET_TYPE_PROCESSOR); + + +static int echo_start (CcnetProcessor *processor, int argc, char **argv); +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void +release_resource(CcnetProcessor *processor) +{ + /* should always chain up */ + CCNET_PROCESSOR_CLASS(ccnet_echo_proc_parent_class)->release_resource (processor); +} + +static void echo_shutdown (CcnetProcessor *processor) +{ + /* The processor is shutdown. */ + + /* the release_resource() will be called after calling shutdown(), + * so only do things that release_resource() does not do. */ + + /* Do not chain up here. */ +} + +static void +ccnet_echo_proc_class_init (CcnetEchoProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->name = "echo-proc"; + proc_class->start = echo_start; + proc_class->handle_response = handle_response; + proc_class->shutdown = echo_shutdown; + proc_class->release_resource = release_resource; +} + +static void +ccnet_echo_proc_init (CcnetEchoProc *processor) +{ +} + +static int +echo_start (CcnetProcessor *processor, int argc, char **argv) +{ + if (IS_SLAVE(processor)) { + char buf[256]; + int len; + len = sprintf (buf, "%s %s", processor->session->base.name, + processor->session->base.id); + ccnet_processor_send_response (processor, + "300", "", buf, len + 1); + return 0; + } + + ccnet_processor_send_request (processor, "echo"); + return 0; +} + + +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + if (memcmp(code, "300", 3) == 0) { + if (content[clen-1] != '\0') { + ccnet_processor_done (processor, FALSE); + return; + } + fprintf (stderr, "Receive echo response: %s\n", content); + ccnet_processor_done (processor, TRUE); + } else { + /* code and code_msg are ended with '\0' */ + ccnet_warning ("Bad response from peer %s(%.8s), %s:%s\n", + processor->peer->name, processor->peer->id, + code, code_msg); + ccnet_processor_done (processor, FALSE); + } +} + diff --git a/net/common/processors/echo-proc.h b/net/common/processors/echo-proc.h new file mode 100644 index 0000000..15d2e9f --- /dev/null +++ b/net/common/processors/echo-proc.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_ECHO_PROC_H +#define CCNET_ECHO_PROC_H + +#include + +#include "processor.h" + +#define CCNET_TYPE_ECHO_PROC (ccnet_echo_proc_get_type ()) +#define CCNET_ECHO_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_ECHO_PROC, CcnetEchoProc)) +#define CCNET_IS_ECHO_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_ECHO_PROC)) +#define CCNET_ECHO_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_ECHO_PROC, CcnetEchoProcClass)) +#define CCNET_IS_ECHO_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_ECHO_PROC)) +#define CCNET_ECHO_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_ECHO_PROC, CcnetEchoProcClass)) + +typedef struct _CcnetEchoProc CcnetEchoProc; +typedef struct _CcnetEchoProcClass CcnetEchoProcClass; + +struct _CcnetEchoProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetEchoProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_echo_proc_get_type (); + +#endif diff --git a/net/common/processors/getpubinfo-proc.c b/net/common/processors/getpubinfo-proc.c new file mode 100644 index 0000000..ba007fb --- /dev/null +++ b/net/common/processors/getpubinfo-proc.c @@ -0,0 +1,72 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "peer.h" +#include "getpubinfo-proc.h" + +#define DEBUG_FLAG CCNET_DEBUG_CONNECTION +#include "log.h" + + +/* + * get peer's public info + */ + +static int get_pubinfo_start (CcnetProcessor *processor, + int argc, char **argv); +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + + +G_DEFINE_TYPE (CcnetGetpubinfoProc, ccnet_getpubinfo_proc, CCNET_TYPE_PROCESSOR) + +static void +ccnet_getpubinfo_proc_class_init (CcnetGetpubinfoProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + /* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */ + + proc_class->name = "getpubinfo-proc"; + proc_class->start = get_pubinfo_start; + proc_class->handle_response = handle_response; +} + +static void +ccnet_getpubinfo_proc_init (CcnetGetpubinfoProc *processor) +{ +} + + +static int get_pubinfo_start (CcnetProcessor *processor, + int argc, char **argv) +{ + ccnet_processor_send_request (processor, "put-pubinfo"); + + return 0; +} + + +static void parse_pubinfo (CcnetProcessor *processor, + char *content, + int clen) +{ + g_return_if_fail (content[clen-1] == '\0'); + ccnet_peer_update_from_string (processor->peer, content); +} + +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + if (code[0] != '2') { + ccnet_warning ("[Getpubinfo] Receive bad response %s: %s\n", + code, code_msg); + ccnet_processor_done (processor, FALSE); + return; + } + + parse_pubinfo (processor, content, clen); + ccnet_processor_done (processor, TRUE); +} diff --git a/net/common/processors/getpubinfo-proc.h b/net/common/processors/getpubinfo-proc.h new file mode 100644 index 0000000..214ad45 --- /dev/null +++ b/net/common/processors/getpubinfo-proc.h @@ -0,0 +1,29 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_GETPUBINFO_PROC_H +#define CCNET_GETPUBINFO_PROC_H + +#include +#include "processor.h" + +#define CCNET_TYPE_GETPUBINFO_PROC (ccnet_getpubinfo_proc_get_type ()) +#define CCNET_GETPUBINFO_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_GETPUBINFO_PROC, CcnetGetpubinfoProc)) +#define CCNET_IS_GETPUBINFO_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_GETPUBINFO_PROC)) +#define CCNET_GETPUBINFO_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_GETPUBINFO_PROC, CcnetGetpubinfoProcClass)) +#define CCNET_IS_GETPUBINFO_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_GETPUBINFO_PROC)) +#define CCNET_GETPUBINFO_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_GETPUBINFO_PROC, CcnetGetpubinfoProcClass)) + +typedef struct _CcnetGetpubinfoProc CcnetGetpubinfoProc; +typedef struct _CcnetGetpubinfoProcClass CcnetGetpubinfoProcClass; + +struct _CcnetGetpubinfoProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetGetpubinfoProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_getpubinfo_proc_get_type (); + +#endif diff --git a/net/common/processors/keepalive-proc.c b/net/common/processors/keepalive-proc.c new file mode 100644 index 0000000..609d102 --- /dev/null +++ b/net/common/processors/keepalive-proc.c @@ -0,0 +1,843 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include +#include +#include + + +#include "keepalive-proc.h" + +#include "peer.h" +#include "session.h" +#include "peer-mgr.h" + +#include "processor.h" +#include "proc-factory.h" + +#include "timer.h" + +#include "rsa.h" +#include "string-util.h" +#include "utils.h" + +#define DEBUG_FLAG CCNET_DEBUG_CONNECTION +#include "log.h" + +/* Since we use only tcp, packet should not be lost, MAX_NUM_RETRY set to 1, + and set a large time (3min). */ +#define KEEPALIVE_INTERVAL 180000 /* 3min */ +#define MAX_NUM_RETRY 1 + +#define MY_VERSION 2 + +/* Select the version according to version used in the other peer. + * -1 if v_other is not supported. + */ +static int get_used_version(int v_other) +{ + if (v_other != 2) + return -1; + return 2; +} + +enum { + INIT, + WAIT_PUBKEY, + WAIT_CHALLENGE, + WAIT_KEEPALIVE, + WAIT_PUBKEY_USER, + WAIT_CHALLENGE_USER, + FULL +}; + + + +/* + protocol v1: + state + keepalive v2 + INIT -----------------> x (packet loss) + + <----------------- start keepalive + OK v2 + + 310 + WAIT_PUBKEY -----------------> + (optional) <----------------- + + 311 random_buf + WAIT_CHALLENGE -----------------> + <----------------- + + + 320 + WAIT_PUBKEY_USER -----------------> + (optional) <----------------- + + 321 random_buf + WAIT_CHALLENGE_USER -----------------> + <----------------- + + + 300 auth-state + WAIT_KEEPALIVE -----------------> + <----------------- + OK + + FULL (keepalive interval) + + 300 auth-state + WAIT_KEEPALIVE -----------------> + <----------------- + OK + + FULL (keepalive interval) + + .... + */ + + +#define SC_BAD_KEEPALIVE "400" +#define SS_BAD_KEEPALIVE "Bad keepalive format" +#define SC_BAD_CHALLENGE "411" +#define SS_BAD_CHALLENGE "Bad challenge format" +#define SC_DECRYPT_ERROR "412" +#define SS_DECRYPT_ERROR "Decrypt error" + + +typedef struct { + unsigned char random_buf[40]; + int used_version; + int count; +} CcnetKeepaliveProcPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_KEEPALIVE_PROC, CcnetKeepaliveProcPriv)) + +#define USE_PRIV CcnetKeepaliveProcPriv *priv = GET_PRIV (processor); + + +G_DEFINE_TYPE (CcnetKeepaliveProc, ccnet_keepalive_proc, CCNET_TYPE_PROCESSOR) + + +static int keepalive_start (CcnetProcessor *processor, + int argc, char **argv); +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void reset_timeout(CcnetProcessor *processor); + +static void proc_shutdown (CcnetProcessor *processor) +{ + CcnetPeer *peer = processor->peer; + + if (IS_SLAVE(processor)) + return; + + if (peer->net_state == PEER_INDIRECT) { + /* In indirect connection, we may receive SC_PROC_DEAD, + * which cause shutdown be called. + */ + /* detach from the peer */ + ccnet_peer_remove_processor (processor->peer, processor); + ccnet_peer_shutdown (peer); + peer->num_fails++; + } else { + /* Otherwise the shutdown of keepalive is only be called in + * peer shutdown */ + } +} + +static void +release_resource(CcnetProcessor *processor) +{ + processor->peer->keepalive_sending = 0; + + /* should always chain up */ + CCNET_PROCESSOR_CLASS(ccnet_keepalive_proc_parent_class)->release_resource (processor); +} + +static void +ccnet_keepalive_proc_class_init (CcnetKeepaliveProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->name = "keepalive-proc"; + proc_class->start = keepalive_start; + proc_class->handle_response = handle_response; + proc_class->handle_update = handle_update; + proc_class->shutdown = proc_shutdown; + proc_class->release_resource = release_resource; + + g_type_class_add_private (klass, sizeof(CcnetKeepaliveProcPriv)); +} + +static void +ccnet_keepalive_proc_init (CcnetKeepaliveProc *processor) +{ +} + + + +static void send_keepalive(CcnetProcessor *processor) +{ + USE_PRIV; + + CcnetPeer *peer = processor->peer; + CcnetUser *user = ccnet_peer_get_user(peer); + + GString *buf = g_string_new(NULL); + GString *cntstr = g_string_new(NULL); + + g_string_append_printf(cntstr, "%d", priv->count++); + + g_string_append_printf (buf, "timestamp %"G_GINT64_FORMAT"\n", + processor->session->timestamp); + if (user) + g_string_append_printf (buf, "role-timestamp %"G_GINT64_FORMAT"\n", + user->myrole_timestamp); + + ccnet_processor_send_update (processor, "300", cntstr->str, + buf->str, buf->len + 1); + /* ccnet_debug ("[Keepalive] Send keepavlie to peer %.8s #%s\n", */ + /* processor->peer->id, cntstr->str); */ + g_string_free (buf, TRUE); + g_string_free (cntstr, TRUE); + + processor->state = WAIT_KEEPALIVE; +} + +static void send_request(CcnetProcessor *processor) +{ + char buf[64]; + snprintf (buf, 64, "keepalive v%d", MY_VERSION); + ccnet_processor_send_request (processor, buf); +} + +static void close_processor_in_timeout(CcnetProcessor *processor) +{ + CcnetPeer *peer = processor->peer; + + ccnet_debug ("[Conn] keepalive timeout current state is %d\n", + processor->state); + + ccnet_processor_done (processor, FALSE); + ccnet_peer_shutdown (peer); + peer->num_fails++; +} + +static int timeout_cb(CcnetProcessor *processor) +{ + /* Since we set MAX_NUM_RETRY to 1, actually, there will be no retry. */ + if (processor->state == INIT) { + if (++processor->num_retry < MAX_NUM_RETRY) + { + send_request(processor); + return TRUE; + } else { + close_processor_in_timeout(processor); + return FALSE; + } + } + + if (processor->state == WAIT_PUBKEY || processor->state == WAIT_CHALLENGE) + { + close_processor_in_timeout(processor); + return FALSE; + } + + if (processor->state == WAIT_KEEPALIVE) { + if (++processor->num_retry < MAX_NUM_RETRY) { + send_keepalive (processor); + return TRUE; + } else { + close_processor_in_timeout(processor); + return FALSE; + } + } + + if (processor->state == FULL) { + processor->num_retry = 0; + send_keepalive(processor); + return TRUE; + } + + return FALSE; +} + +static void reset_timeout(CcnetProcessor *processor) +{ + if (processor->retry_timer) + ccnet_timer_free(&processor->retry_timer); + processor->retry_timer = ccnet_timer_new ((TimerCB)timeout_cb, processor, + KEEPALIVE_INTERVAL); +} + + + +static void close_processor(CcnetProcessor *processor) +{ + CcnetPeer *peer = processor->peer; + + ccnet_timer_free (&processor->retry_timer); + ccnet_processor_done (processor, FALSE); + ccnet_peer_shutdown (peer); + peer->num_fails++; +} + + +static int keepalive_start (CcnetProcessor *processor, + int argc, char **argv) +{ + CcnetKeepaliveProcPriv *priv = GET_PRIV (processor); + + if (IS_SLAVE(processor)) { + char buf[16]; + int v, len; + + if (argc == 0) { + priv->used_version = 0; + ccnet_processor_send_response (processor, + SC_VERSION_MISMATCH, SS_VERSION_MISMATCH, NULL, 0); + ccnet_processor_done (processor, FALSE); + return 0; + } else { + v = get_version(argv[0]); + if ((priv->used_version = get_used_version(v)) == -1) { + ccnet_processor_send_response (processor, + SC_VERSION_MISMATCH, SS_VERSION_MISMATCH, NULL, 0); + ccnet_processor_done (processor, FALSE); + return 0; + } + + len = snprintf (buf, 16, "v%d", priv->used_version); + ccnet_processor_send_response (processor, + SC_OK, SS_OK, buf, len + 1); + return 0; + } + } + + /* master */ + priv->count = 0; + processor->state = INIT; + processor->peer->keepalive_sending = 1; + send_request (processor); + reset_timeout (processor); + + return 0; +} + +struct Handler +{ + const char *code; + void (*handler) (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); +}; + +struct Handler * +get_handler (char *code, struct Handler *tab) +{ + struct Handler *c; + + for (c = tab; c->code; c++) { + if (c->code[0] == code[0] && c->code[1] == code[1] + && c->code[2] == code[2]) + return c; + } + + return NULL; +} + + +static void recv_keepalive_rsp(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void recv_ok(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void recv_pubkey(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void verify_challenge(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void recv_pubkey_user(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void verify_challenge_user(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static struct Handler rsp_handler_tab[] = { + { "200", recv_ok }, + { "300", recv_keepalive_rsp }, + { "310", recv_pubkey }, + { "311", verify_challenge }, + { "320", recv_pubkey_user }, + { "321", verify_challenge_user }, + { 0 }, +}; + +static void send_challenge(CcnetProcessor *processor) +{ + CcnetKeepaliveProcPriv *priv = GET_PRIV (processor); + + ccnet_debug ("[Keepalive] Send peer challenge to %s(%.8s)\n", + processor->peer->name, processor->peer->id); + + CcnetPeer *peer = processor->peer; + unsigned char *buf; + int len; + + RAND_pseudo_bytes (priv->random_buf, 40); + buf = public_key_encrypt (peer->pubkey, priv->random_buf, 40, &len); + ccnet_processor_send_update (processor, "311", NULL, (char *)buf, len); + + g_free(buf); + processor->state = WAIT_CHALLENGE; + reset_timeout (processor); +} + +static void get_pubkey(CcnetProcessor *processor) +{ + ccnet_processor_send_update (processor, + "310", NULL, NULL, 0); + processor->state = WAIT_PUBKEY; + reset_timeout(processor); +} + +static void get_pubkey_user(CcnetProcessor *processor) +{ + ccnet_processor_send_update (processor, + "320", NULL, NULL, 0); + processor->state = WAIT_PUBKEY_USER; + reset_timeout(processor); +} + +static void send_challenge_user(CcnetProcessor *processor, CcnetUser *user) +{ + CcnetKeepaliveProcPriv *priv = GET_PRIV (processor); + unsigned char *buf; + int len; + + ccnet_debug ("[Keepalive] Send user challenge to %.8s\n", + processor->peer->id); + RAND_pseudo_bytes (priv->random_buf, 40); + buf = public_key_encrypt (user->pubkey, priv->random_buf, 40, &len); + ccnet_processor_send_update (processor, "321", NULL, (char *)buf, len); + + g_free(buf); + processor->state = WAIT_CHALLENGE_USER; + reset_timeout (processor); +} + + +static void recv_ok(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + USE_PRIV; + + if (processor->state != INIT) { + close_processor(processor); + return; + } + + /* check version */ + if (clen != 0) { + int v = get_version(content); + if ((priv->used_version = get_used_version(v)) == -1) { + ccnet_processor_send_error_update(processor, SC_VERSION_MISMATCH, + SS_VERSION_MISMATCH); + close_processor(processor); + return; + } + } else { + ccnet_processor_send_error_update(processor, SC_VERSION_MISMATCH, + SS_VERSION_MISMATCH); + close_processor(processor); + return; + } + + if (processor->peer->net_state == PEER_DOWN) + ccnet_peer_set_net_state (processor->peer, PEER_INDIRECT); + + if (processor->peer->pubkey) + send_challenge(processor); + else + get_pubkey(processor); +} + +static void recv_pubkey(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + if (clen == 0 || content[clen-1] != '\0') { + ccnet_debug ("[Conn] Bad public key format\n"); + close_processor (processor); + return; + } + + ccnet_peer_set_pubkey (processor->peer, content); + if (processor->peer->pubkey == NULL) { + ccnet_debug ("[Conn] Bad public key format\n"); + close_processor (processor); + return; + } + + send_challenge (processor); +} + +static void recv_keepalive_rsp(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + /* ccnet_debug ("[Keepalive] Receive keepalive responese from peer %.8s #%s\n", */ + /* processor->peer->id, code_msg); */ + processor->state = FULL; + if (processor->peer->net_state == PEER_CONNECTED + && processor->peer->num_fails > 0) + processor->peer->num_fails--; +} + +static void verify_challenge(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + CcnetKeepaliveProcPriv *priv = GET_PRIV (processor); + ccnet_debug ("[Conn] Verify Peer Challenge\n"); + + if (clen != 40 || memcmp(content, priv->random_buf, 40) != 0) { + ccnet_debug ("[Conn] Peer Challenge failed\n"); + close_processor(processor); + return; + } + + CcnetUser *user = ccnet_peer_get_user(processor->peer); + if (!user) { + ccnet_debug ("[Conn] No user for this peer, go to auth done\n"); + processor->peer->auth_done = 1; + g_signal_emit_by_name (processor->peer, "auth-done"); + + send_keepalive (processor); + reset_timeout (processor); + return; + } + + if (user->pubkey) + send_challenge_user(processor, user); + else + get_pubkey_user(processor); +} + +static void recv_pubkey_user(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + if (clen == 0 || content[clen-1] != '\0') { + ccnet_debug ("[Conn] Bad public key format\n"); + close_processor (processor); + return; + } + + CcnetUser *user = ccnet_peer_get_user(processor->peer); + ccnet_user_set_pubkey (user, content); + + if (user->pubkey == NULL) { + ccnet_debug ("[Conn] Bad public key format\n"); + close_processor (processor); + return; + } + + send_challenge_user (processor, user); +} + +static void verify_challenge_user(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + CcnetKeepaliveProcPriv *priv = GET_PRIV (processor); + ccnet_debug ("[Conn] Verify User Challenge\n"); + + if (clen != 40 || memcmp(content, priv->random_buf, 40) != 0) { + ccnet_debug ("[Keepalive] Challenge failed\n"); + close_processor(processor); + return; + } + + processor->peer->auth_done = 1; + g_signal_emit_by_name (processor->peer, "auth-done"); + + send_keepalive (processor); + reset_timeout (processor); +} + +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + if (code[0] == '4' || code[0] == '5') { + ccnet_warning ("[Keepalive] Error from peer %s %s\n", + code, code_msg); + close_processor (processor); + return; + } + + struct Handler *handler = get_handler(code, rsp_handler_tab); + if (!handler) { + ccnet_processor_send_update (processor, SC_BAD_RESPONSE_CODE, + SS_BAD_RESPONSE_CODE, NULL, 0); + close_processor (processor); + return; + } + + handler->handler(processor, code, code_msg, content, clen); +} + +/* +static void start_keepalive (CcnetProcFactory *factory, CcnetPeer *peer) +{ + CcnetProcessor *processor; + + processor = ccnet_proc_factory_create_master_processor ( + factory, "keepalive", peer); + + if (processor == NULL) { + ccnet_warning ("Create keepalive processor failed\n"); + return; + } + ccnet_processor_startl (processor, NULL); +} +*/ + + +/* update handle */ + +static void receive_keepalive(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void send_pubkey(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void response_challenge(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void send_pubkey_user(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void response_challenge_user(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static struct Handler update_handler_tab[] = { + { "300", receive_keepalive }, + { "310", send_pubkey }, + { "311", response_challenge }, + { "320", send_pubkey_user }, + { "321", response_challenge_user }, + { 0 }, +}; + + + +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + if (code[0] == '4' || code[0] == '5') { + ccnet_warning ("[Keepalive] Error from peer %s %s\n", + code, code_msg); + ccnet_processor_done(processor, FALSE); + return; + } + + struct Handler *handler = get_handler(code, update_handler_tab); + if (!handler) { + ccnet_processor_send_response (processor, + SC_BAD_UPDATE_CODE, SS_BAD_UPDATE_CODE, NULL, 0); + ccnet_processor_done(processor, FALSE); + return; + } + handler->handler(processor, code, code_msg, content, clen); +} + +static void get_peer_pubinfo (CcnetPeer *peer) +{ + CcnetProcessor *newp; + CcnetProcFactory *factory = peer->manager->session->proc_factory; + + newp = ccnet_proc_factory_create_master_processor (factory, + "get-pubinfo", peer); + if (newp == NULL) { + ccnet_warning ("Create get pubinfo processor failed\n"); + return; + } + ccnet_processor_startl (newp, NULL); +} + + +static void get_user_pubinfo (CcnetPeer *peer) +{ + CcnetProcessor *newp; + CcnetProcFactory *factory = peer->manager->session->proc_factory; + + newp = ccnet_proc_factory_create_master_processor (factory, + "get-user", peer); + if (newp == NULL) { + ccnet_warning ("Create get user info processor failed\n"); + return; + } + ccnet_processor_startl (newp, NULL); +} + + +static void update_from_key_value (void *vpeer, + const char *key, + const char *value) +{ + CcnetPeer *peer = vpeer; + + if (strcmp(key, "timestamp") == 0) { + gint64 timestamp = g_ascii_strtoll(value, NULL, 10); + if (timestamp > peer->timestamp) + get_peer_pubinfo (peer); + CcnetUser *user = ccnet_peer_get_user(peer); + if (!user || timestamp > user->timestamp) + get_user_pubinfo (peer); + } + + if (strcmp(key, "role-timestamp") == 0) { + CcnetUser *user = ccnet_peer_get_user(peer); + if (!user) + return; + gint64 timestamp = g_ascii_strtoll (value, NULL, 10); + if (timestamp < user->role_timestamp) { + ccnet_peer_manager_notify_peer_role (peer->manager, peer); + } + } +} + + +static void receive_keepalive(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + CcnetPeer *peer = processor->peer; + + if (clen == 0 || content[clen-1] != '\0' || content[clen-2] != '\n') + { + ccnet_processor_send_response ( + processor, SC_BAD_KEEPALIVE, SS_BAD_KEEPALIVE, NULL, 0); + ccnet_processor_done (processor, FALSE); + return; + } + + parse_key_value_pairs (content, update_from_key_value, peer); + + /* ccnet_debug ("[Keepalive] Receive keepalive from %.8s #%s\n", */ + /* processor->peer->id, code_msg); */ + ccnet_processor_send_response ( + processor, code, code_msg, NULL, 0); + + /* Peer discovered us, so we try to discover peer too. + * Used in indirect connection vie Relay */ + /* + if (peer->net_state == PEER_DOWN && peer->relay_list != NULL) { + if (!peer->keepalive_sending) + start_keepalive (processor->session->proc_factory, peer); + } + */ +} + +static void send_pubkey(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + GString *str; + + str = public_key_to_gstring(processor->session->pubkey); + ccnet_processor_send_response (processor, code, "", str->str, str->len+1); +} + + +static void response_challenge(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + unsigned char *buf; + int decrypt_len; + + if (clen == 0) { + ccnet_warning("Peer %s(%.8s) send bad format challenge\n", + processor->peer->name, processor->peer->id); + ccnet_processor_send_response ( + processor, SC_BAD_CHALLENGE, SS_BAD_CHALLENGE, NULL, 0); + ccnet_processor_done(processor, FALSE); + return; + } + + buf = private_key_decrypt(processor->session->privkey, + (unsigned char *)content, clen, &decrypt_len); + if (decrypt_len < 0) { + ccnet_processor_send_response ( + processor, SC_DECRYPT_ERROR, SS_DECRYPT_ERROR, NULL, 0); + ccnet_processor_done(processor, FALSE); + } else + ccnet_processor_send_response ( + processor, code, "", (char *)buf, decrypt_len); + g_free(buf); +} + +static void send_pubkey_user(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + GString *str; + + str = public_key_to_gstring(processor->session->user_pubkey); + ccnet_processor_send_response (processor, code, "", str->str, str->len+1); +} + +static void response_challenge_user(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + unsigned char *buf; + int decrypt_len; + + if (clen == 0) { + ccnet_warning("Peer %s(%.8s) send bad format challenge\n", + processor->peer->name, processor->peer->id); + ccnet_processor_send_response ( + processor, SC_BAD_CHALLENGE, SS_BAD_CHALLENGE, NULL, 0); + ccnet_processor_done(processor, FALSE); + return; + } + + buf = private_key_decrypt(processor->session->user_privkey, + (unsigned char *)content, clen, &decrypt_len); + if (decrypt_len < 0) { + ccnet_processor_send_response ( + processor, SC_DECRYPT_ERROR, SS_DECRYPT_ERROR, NULL, 0); + ccnet_processor_done(processor, FALSE); + } else + ccnet_processor_send_response ( + processor, code, "", (char *)buf, decrypt_len); + g_free(buf); +} diff --git a/net/common/processors/keepalive-proc.h b/net/common/processors/keepalive-proc.h new file mode 100644 index 0000000..3ff6479 --- /dev/null +++ b/net/common/processors/keepalive-proc.h @@ -0,0 +1,29 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_GETPEERINFO_PROC_H +#define CCNET_GETPEERINFO_PROC_H + +#include +#include "processor.h" + +#define CCNET_TYPE_KEEPALIVE_PROC (ccnet_keepalive_proc_get_type ()) +#define CCNET_KEEPALIVE_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_KEEPALIVE_PROC, CcnetKeepaliveProc)) +#define CCNET_IS_KEEPALIVE_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_KEEPALIVE_PROC)) +#define CCNET_KEEPALIVE_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_KEEPALIVE_PROC, CcnetKeepaliveProcClass)) +#define CCNET_IS_KEEPALIVE_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_KEEPALIVE_PROC)) +#define CCNET_KEEPALIVE_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_KEEPALIVE_PROC, CcnetKeepaliveProcClass)) + +typedef struct _CcnetKeepaliveProc CcnetKeepaliveProc; +typedef struct _CcnetKeepaliveProcClass CcnetKeepaliveProcClass; + +struct _CcnetKeepaliveProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetKeepaliveProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_keepalive_proc_get_type (); + +#endif diff --git a/net/common/processors/keepalive2-proc.c b/net/common/processors/keepalive2-proc.c new file mode 100644 index 0000000..d3e799e --- /dev/null +++ b/net/common/processors/keepalive2-proc.c @@ -0,0 +1,599 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include +#include +#include + + +#include "keepalive2-proc.h" + +#include "peer.h" +#include "session.h" +#include "peer-mgr.h" + +#include "processor.h" +#include "proc-factory.h" + +#include "timer.h" + +#include "rsa.h" +#include "string-util.h" +#include "utils.h" + +#define DEBUG_FLAG CCNET_DEBUG_CONNECTION +#include "log.h" + +/* Since we use only tcp, packet should not be lost, + set a large time (3min). */ +#define KEEPALIVE_INTERVAL 180000 /* 3min */ + +enum { + INIT, + WAIT_PUBKEY, + WAIT_CHALLENGE, + WAIT_KEEPALIVE, + FULL +}; + + +/* + protocol: + state + keepalive2 + INIT -----------------> + + <----------------- start keepalive + OK + + 310 + WAIT_PUBKEY -----------------> + (optional) <----------------- + + 311 random_buf + WAIT_CHALLENGE -----------------> + <----------------- + + + 300 + WAIT_KEEPALIVE -----------------> + <----------------- + OK + + FULL (keepalive interval) + + 300 + WAIT_KEEPALIVE -----------------> + <----------------- + OK + + FULL (keepalive interval) + + .... + */ + + +#define SC_BAD_KEEPALIVE "400" +#define SS_BAD_KEEPALIVE "Bad keepalive format" +#define SC_BAD_CHALLENGE "411" +#define SS_BAD_CHALLENGE "Bad challenge format" +#define SC_DECRYPT_ERROR "412" +#define SS_DECRYPT_ERROR "Decrypt error" + + +typedef struct { + unsigned char random_buf[40]; + int count; +} CcnetKeepalive2ProcPriv; + +extern CcnetSession *session; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_KEEPALIVE2_PROC, CcnetKeepalive2ProcPriv)) + +#define USE_PRIV CcnetKeepalive2ProcPriv *priv = GET_PRIV (processor); + + +G_DEFINE_TYPE (CcnetKeepalive2Proc, ccnet_keepalive2_proc, CCNET_TYPE_PROCESSOR) + + +static int keepalive2_start (CcnetProcessor *processor, + int argc, char **argv); +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); +static void reset_timeout(CcnetProcessor *processor); +static void get_peer_pubinfo (CcnetPeer *peer); + +static void proc_shutdown (CcnetProcessor *processor) +{ + CcnetPeer *peer = processor->peer; + + if (IS_SLAVE(processor)) + return; + + /* The shutdown of keepalive is only be called in + * peer shutdown */ + /* g_assert (peer->in_shutdown); */ + if (!peer->in_shutdown) + ccnet_warning ("Shutdown keepalive is not called from peer_shutdown\n"); +} + +static void +release_resource(CcnetProcessor *processor) +{ + processor->peer->keepalive_sending = 0; + + /* should always chain up */ + CCNET_PROCESSOR_CLASS(ccnet_keepalive2_proc_parent_class)->release_resource (processor); +} + +static void +ccnet_keepalive2_proc_class_init (CcnetKeepalive2ProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->name = "keepalive2-proc"; + proc_class->start = keepalive2_start; + proc_class->handle_response = handle_response; + proc_class->handle_update = handle_update; + proc_class->shutdown = proc_shutdown; + proc_class->release_resource = release_resource; + + g_type_class_add_private (klass, sizeof(CcnetKeepalive2ProcPriv)); +} + +static void +ccnet_keepalive2_proc_init (CcnetKeepalive2Proc *processor) +{ +} + +static void send_keepalive(CcnetProcessor *processor) +{ + USE_PRIV; + + char cntstr[64]; + + sprintf(cntstr, "%d", priv->count++); + ccnet_processor_send_update (processor, "300", cntstr, + NULL, 0); + /* ccnet_debug ("[Keepalive] Send keepavlie to peer %.8s #%s\n", */ + /* processor->peer->id, cntstr->str); */ + + processor->state = WAIT_KEEPALIVE; +} + +static void send_request(CcnetProcessor *processor) +{ + char buf[64]; + snprintf (buf, 64, "keepalive2"); + ccnet_processor_send_request (processor, buf); +} + +static void close_processor_in_timeout(CcnetProcessor *processor) +{ + CcnetPeer *peer = processor->peer; + + ccnet_debug ("[Conn] keepalive timeout current state is %d\n", + processor->state); + + ccnet_processor_done (processor, FALSE); + ccnet_peer_shutdown (peer); + peer->num_fails++; +} + +static int timeout_cb(CcnetProcessor *processor) +{ + if (processor->state == FULL) { + send_keepalive(processor); + return TRUE; + } + + close_processor_in_timeout(processor); + return FALSE; +} + +static void reset_timeout(CcnetProcessor *processor) +{ + if (processor->retry_timer) + ccnet_timer_free(&processor->retry_timer); + processor->retry_timer = ccnet_timer_new ((TimerCB)timeout_cb, processor, + KEEPALIVE_INTERVAL); +} + +static void close_processor(CcnetProcessor *processor) +{ + CcnetPeer *peer = processor->peer; + + ccnet_timer_free (&processor->retry_timer); + ccnet_processor_done (processor, FALSE); + ccnet_peer_shutdown (peer); + peer->num_fails++; +} + + +static int keepalive2_start (CcnetProcessor *processor, + int argc, char **argv) +{ + CcnetKeepalive2ProcPriv *priv = GET_PRIV (processor); + + if (IS_SLAVE(processor)) { + ccnet_processor_send_response (processor, + SC_OK, SS_OK, NULL, 0); + return 0; + } + + /* master */ + priv->count = 0; + processor->state = INIT; + processor->peer->keepalive_sending = 1; + send_request (processor); + reset_timeout (processor); + + return 0; +} + +struct Handler +{ + const char *code; + void (*handler) (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); +}; + +struct Handler * +get_handler (char *code, struct Handler *tab) +{ + struct Handler *c; + + for (c = tab; c->code; c++) { + if (c->code[0] == code[0] && c->code[1] == code[1] + && c->code[2] == code[2]) + return c; + } + + return NULL; +} + + +static void recv_keepalive_rsp(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void recv_ok(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void recv_pubkey(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void verify_challenge(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static struct Handler rsp_handler_tab[] = { + { "200", recv_ok }, + { "300", recv_keepalive_rsp }, + { "310", recv_pubkey }, + { "311", verify_challenge }, + { 0 }, +}; + +static void send_challenge(CcnetProcessor *processor) +{ + CcnetKeepalive2ProcPriv *priv = GET_PRIV (processor); + + ccnet_debug ("[Keepalive] Before send challenge, check valid of id\n"); + char *id = id_from_pubkey(processor->peer->pubkey); + if (g_strcmp0(id, processor->peer->id) != 0) { + ccnet_debug ("[Keepalive] Peer id not conform to public key: %s %s\n", + id, processor->peer->id); + close_processor (processor); + g_free (id); + return; + } + g_free (id); + + ccnet_debug ("[Keepalive] Send peer challenge to %s(%.8s)\n", + processor->peer->name, processor->peer->id); + + CcnetPeer *peer = processor->peer; + unsigned char *buf; + int len; + + RAND_pseudo_bytes (priv->random_buf, 40); + buf = public_key_encrypt (peer->pubkey, priv->random_buf, 40, &len); + if (len < 0) { + ccnet_debug ("[Keepalive] Failed to encrypt challenge " + "with peer %s(%.8s)'s pubkey\n", peer->name, peer->id); + close_processor (processor); + g_free (buf); + return; + } + + ccnet_processor_send_update (processor, "311", NULL, (char *)buf, len); + + g_free(buf); + processor->state = WAIT_CHALLENGE; + reset_timeout (processor); +} + +static void get_pubkey(CcnetProcessor *processor) +{ + ccnet_processor_send_update (processor, + "310", NULL, NULL, 0); + processor->state = WAIT_PUBKEY; + reset_timeout(processor); +} + +static void recv_ok(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + if (processor->state != INIT) { + close_processor(processor); + return; + } + + if (processor->peer->pubkey) { + ccnet_debug ("[Keepalive] Receive ok, send challenge\n"); + send_challenge(processor); + } else { + ccnet_debug ("[Keepalive] Receive ok, get pubkey\n"); + get_pubkey(processor); + } +} + +static void recv_pubkey(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + if (processor->state != WAIT_PUBKEY) { + ccnet_debug ("[Keepalive] Receive public key not in WAIT_PUBKEY state\n"); + close_processor (processor); + return; + } + + if (clen == 0 || content[clen-1] != '\0') { + ccnet_debug ("[Keepalive] Bad public key format\n"); + close_processor (processor); + return; + } + + ccnet_peer_set_pubkey (processor->peer, content); + if (processor->peer->pubkey == NULL) { + ccnet_debug ("[Keepalive] Bad public key format\n"); + close_processor (processor); + return; + } + + ccnet_debug ("[Keepalive] Receive pubkey, send challenge\n"); + send_challenge (processor); +} + +static void recv_keepalive_rsp(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + /* ccnet_debug ("[Keepalive] Receive keepalive responese from peer %.8s #%s\n", */ + /* processor->peer->id, code_msg); */ + processor->state = FULL; +} + +static void on_send_skey_done (CcnetProcessor *processor, + gboolean success, void *data) +{ + if (!success && !processor->peer->in_shutdown) { + /* try the old version */ + CcnetProcessor *p; + CcnetProcFactory *factory = processor->session->proc_factory; + CcnetPeer *peer = processor->peer; + + if (peer->session_key) { + ccnet_warning ("peer %s already has session key\n", peer->id); + return; + } + + p = ccnet_proc_factory_create_master_processor ( + factory, "send-session-key", peer); + if (!p) { + ccnet_warning ("create send session key processor failed\n"); + return; + } + + if (ccnet_processor_startl (p, NULL) < 0) { + ccnet_warning ("start send session key processor failed\n"); + return; + } + } +} + +static void +send_session_key (CcnetPeer *peer) +{ + CcnetProcessor *processor; + CcnetProcFactory *factory = peer->manager->session->proc_factory; + + if (peer->session_key) { + ccnet_warning ("peer %s already has session key\n", peer->id); + return; + } + + processor = ccnet_proc_factory_create_master_processor ( + factory, "send-skey2", peer); + if (!processor) { + ccnet_warning ("create send session key processor failed\n"); + return; + } + + g_signal_connect (processor, "done", + G_CALLBACK(on_send_skey_done), NULL); + + if (ccnet_processor_startl (processor, NULL) < 0) { + ccnet_warning ("start send session key processor failed\n"); + return; + } +} + +static void verify_challenge(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + CcnetKeepalive2ProcPriv *priv = GET_PRIV (processor); + + if (clen != 40 || memcmp(content, priv->random_buf, 40) != 0) { + ccnet_debug ("[Conn] Peer Challenge failed\n"); + close_processor(processor); + return; + } + + ccnet_debug ("[Keepalive] Verify Peer Challenge\n"); + + get_peer_pubinfo (processor->peer); + /* ccnet_peer_manager_notify_peer_role (processor->peer->manager, */ + /* processor->peer); */ + + if (strcmp(session->base.id, processor->peer->id) < 0) + send_session_key (processor->peer); + + send_keepalive (processor); + reset_timeout (processor); + return; +} + +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + if (code[0] == '4' || code[0] == '5') { + ccnet_warning ("[Keepalive] Error from peer %s %s\n", + code, code_msg); + close_processor (processor); + return; + } + + struct Handler *handler = get_handler(code, rsp_handler_tab); + if (!handler) { + ccnet_processor_send_update (processor, SC_BAD_RESPONSE_CODE, + SS_BAD_RESPONSE_CODE, NULL, 0); + close_processor (processor); + return; + } + + handler->handler(processor, code, code_msg, content, clen); +} + + +/* update handle */ + +static void receive_keepalive(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void send_pubkey(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void response_challenge(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + + +static struct Handler update_handler_tab[] = { + { "300", receive_keepalive }, + { "310", send_pubkey }, + { "311", response_challenge }, + { 0 }, +}; + + + +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + if (code[0] == '4' || code[0] == '5') { + ccnet_warning ("[Keepalive] Error from peer %s %s\n", + code, code_msg); + ccnet_processor_done(processor, FALSE); + return; + } + + struct Handler *handler = get_handler(code, update_handler_tab); + if (!handler) { + ccnet_processor_send_response (processor, + SC_BAD_UPDATE_CODE, SS_BAD_UPDATE_CODE, NULL, 0); + ccnet_processor_done(processor, FALSE); + return; + } + handler->handler(processor, code, code_msg, content, clen); +} + +static void get_peer_pubinfo (CcnetPeer *peer) +{ + CcnetProcessor *newp; + CcnetProcFactory *factory = peer->manager->session->proc_factory; + + newp = ccnet_proc_factory_create_master_processor (factory, + "get-pubinfo", peer); + if (newp == NULL) { + ccnet_warning ("Create get pubinfo processor failed\n"); + return; + } + ccnet_processor_startl (newp, NULL); +} + + +static void receive_keepalive(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + /* ccnet_debug ("[Keepalive] Receive keepalive from %.8s #%s\n", */ + /* processor->peer->id, code_msg); */ + ccnet_processor_send_response ( + processor, code, code_msg, NULL, 0); +} + +static void send_pubkey(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + GString *str; + + str = public_key_to_gstring(processor->session->pubkey); + ccnet_processor_send_response (processor, code, "", str->str, str->len+1); +} + + +static void response_challenge(CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + unsigned char *buf; + int decrypt_len; + + if (clen == 0) { + ccnet_warning("Peer %s(%.8s) send bad format challenge\n", + processor->peer->name, processor->peer->id); + ccnet_processor_send_response ( + processor, SC_BAD_CHALLENGE, SS_BAD_CHALLENGE, NULL, 0); + ccnet_processor_done(processor, FALSE); + return; + } + + buf = private_key_decrypt(processor->session->privkey, + (unsigned char *)content, clen, &decrypt_len); + if (decrypt_len < 0) { + ccnet_processor_send_response ( + processor, SC_DECRYPT_ERROR, SS_DECRYPT_ERROR, NULL, 0); + ccnet_processor_done(processor, FALSE); + } else + ccnet_processor_send_response ( + processor, code, "", (char *)buf, decrypt_len); + g_free(buf); +} diff --git a/net/common/processors/keepalive2-proc.h b/net/common/processors/keepalive2-proc.h new file mode 100644 index 0000000..38a08b3 --- /dev/null +++ b/net/common/processors/keepalive2-proc.h @@ -0,0 +1,29 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_KEEPALIVE2_PROC_H +#define CCNET_KEEPALIVE2_PROC_H + +#include +#include "processor.h" + +#define CCNET_TYPE_KEEPALIVE2_PROC (ccnet_keepalive2_proc_get_type ()) +#define CCNET_KEEPALIVE2_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_KEEPALIVE2_PROC, CcnetKeepalive2Proc)) +#define CCNET_IS_KEEPALIVE2_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_KEEPALIVE2_PROC)) +#define CCNET_KEEPALIVE2_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_KEEPALIVE2_PROC, CcnetKeepalive2ProcClass)) +#define CCNET_IS_KEEPALIVE2_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_KEEPALIVE2_PROC)) +#define CCNET_KEEPALIVE2_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_KEEPALIVE2_PROC, CcnetKeepalive2ProcClass)) + +typedef struct _CcnetKeepalive2Proc CcnetKeepalive2Proc; +typedef struct _CcnetKeepalive2ProcClass CcnetKeepalive2ProcClass; + +struct _CcnetKeepalive2Proc { + CcnetProcessor parent_instance; +}; + +struct _CcnetKeepalive2ProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_keepalive2_proc_get_type (); + +#endif diff --git a/net/common/processors/mqserver-proc.c b/net/common/processors/mqserver-proc.c new file mode 100644 index 0000000..4c19333 --- /dev/null +++ b/net/common/processors/mqserver-proc.c @@ -0,0 +1,161 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "peer.h" +#include "message.h" +#include "session.h" +#include "message-manager.h" +#include "mqserver-proc.h" +#include "algorithms.h" + +#define DEBUG_FLAG CCNET_DEBUG_MESSAGE +#include "log.h" + +#define SC_MSG "300" + +enum { + INIT, + READY +}; + +typedef struct { + int n_app; + char **apps; + int subscribed : 1; +} MqserverProcPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_MQSERVER_PROC, MqserverProcPriv)) + +static int mq_server_start (CcnetProcessor *processor, int argc, char **argv); + +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +G_DEFINE_TYPE (CcnetMqserverProc, ccnet_mqserver_proc, CCNET_TYPE_PROCESSOR) + + +static void subscribe_message (CcnetProcessor *processor) +{ + MqserverProcPriv *priv = GET_PRIV (processor); + CcnetMessageManager *msg_mgr = processor->session->msg_mgr; + + priv->subscribed = 1; + ccnet_message_manager_subscribe_app (msg_mgr, processor, + priv->n_app, priv->apps); +} + +static void unsubscribe_message (CcnetProcessor *processor) +{ + MqserverProcPriv *priv = GET_PRIV (processor); + CcnetMessageManager *msg_mgr = processor->session->msg_mgr; + + if (priv->subscribed) + ccnet_message_manager_unsubscribe_app (msg_mgr, processor, + priv->n_app, priv->apps); +} + +static void release_resource (CcnetProcessor *processor) +{ + int i; + MqserverProcPriv *priv = GET_PRIV (processor); + + unsubscribe_message(processor); + + for (i = 0; i < priv->n_app; ++i) + g_free (priv->apps[i]); + g_free (priv->apps); + + memset (priv, 0, sizeof(MqserverProcPriv)); + + CCNET_PROCESSOR_CLASS(ccnet_mqserver_proc_parent_class)->release_resource(processor); +} + + + +static void +ccnet_mqserver_proc_class_init (CcnetMqserverProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + /* GObjectClass *object_class = G_OBJECT_CLASS (klass); */ + + proc_class->name = "mqserver-proc"; + proc_class->start = mq_server_start; + proc_class->handle_update = handle_update; + proc_class->release_resource = release_resource; + + g_type_class_add_private (klass, sizeof (MqserverProcPriv)); +} + +static void +ccnet_mqserver_proc_init (CcnetMqserverProc *processor) +{ +} + + +static int +mq_server_start (CcnetProcessor *processor, int argc, char **argv) +{ + MqserverProcPriv *priv = GET_PRIV (processor); + int i; + + priv->n_app = argc; + priv->apps = g_new (char*, argc); + for (i = 0; i < argc; ++i) + priv->apps[i] = g_strdup (argv[i]); + + subscribe_message (processor); + + ccnet_processor_send_response (processor, "200", "OK", NULL, 0); + return 0; +} + +static void send_message (CcnetProcessor *processor, CcnetMessage *message) +{ + GString *buf = g_string_new (NULL); + + ccnet_message_to_string_buf_local (message, buf); + ccnet_processor_send_response (processor, SC_MSG, NULL, + buf->str, buf->len+1); + g_string_free (buf, TRUE); +} + +void +ccnet_mqserver_proc_put_message (CcnetProcessor *processor, + CcnetMessage *message) +{ + send_message (processor, message); +} + + +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + /* MqserverProcPriv *priv = GET_PRIV (processor); */ + /* CcnetMessageManager *msg_mgr = processor->session->messageMgr; */ + + if (code[0] != '3') { + ccnet_warning ("received bad update: %s %s", code, code_msg); + return; + } + + if (code[2] == '0') { + /* SC_MSG */ + CcnetMessage *msg; + msg = ccnet_message_from_string_local (content, clen); + + /* ccnet_debug ("[msg] send msg: %.10s\n", msg->body); */ + + ccnet_send_message (processor->session, msg); + ccnet_message_unref (msg); + } else if (code[2] == '1') { + /* SC_UNSUBSCRIBE */ + ccnet_processor_done (processor, TRUE); + return; + } + + ccnet_processor_send_response (processor, "200", "OK", NULL, 0); +} diff --git a/net/common/processors/mqserver-proc.h b/net/common/processors/mqserver-proc.h new file mode 100644 index 0000000..0619225 --- /dev/null +++ b/net/common/processors/mqserver-proc.h @@ -0,0 +1,34 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_MQSERVER_PROC_H +#define CCNET_MQSERVER_PROC_H + +#include + +#include "processor.h" +#include "message.h" + +#define CCNET_TYPE_MQSERVER_PROC (ccnet_mqserver_proc_get_type ()) +#define CCNET_MQSERVER_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_MQSERVER_PROC, CcnetMqserverProc)) +#define CCNET_IS_MQSERVER_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_MQSERVER_PROC)) +#define CCNET_MQSERVER_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_MQSERVER_PROC, CcnetMqserverProcClass)) +#define CCNET_IS_MQSERVER_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_MQSERVER_PROC)) +#define CCNET_MQSERVER_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_MQSERVER_PROC, CcnetMqserverProcClass)) + +typedef struct _CcnetMqserverProc CcnetMqserverProc; +typedef struct _CcnetMqserverProcClass CcnetMqserverProcClass; + +struct _CcnetMqserverProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetMqserverProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_mqserver_proc_get_type (); + +void ccnet_mqserver_proc_put_message (CcnetProcessor *processor, + CcnetMessage *message); + +#endif diff --git a/net/common/processors/putpubinfo-proc.c b/net/common/processors/putpubinfo-proc.c new file mode 100644 index 0000000..3a695c1 --- /dev/null +++ b/net/common/processors/putpubinfo-proc.c @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "peer.h" +#include "session.h" +#include "putpubinfo-proc.h" + + +#define DEBUG_FLAG CCNET_DEBUG_CONNECTION +#include "log.h" + + + +static int put_pubinfo_start (CcnetProcessor *processor, int argc, char **argv); +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + + +G_DEFINE_TYPE (CcnetPutpubinfoProc, ccnet_putpubinfo_proc, CCNET_TYPE_PROCESSOR) + + +static void +ccnet_putpubinfo_proc_class_init (CcnetPutpubinfoProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + /* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */ + + proc_class->name = "putpubinfo-proc"; + proc_class->start = put_pubinfo_start; + proc_class->handle_update = handle_update; +} + +static void +ccnet_putpubinfo_proc_init (CcnetPutpubinfoProc *processor) +{ +} + + +static void put_pubinfo (CcnetProcessor *processor) +{ + GString *str = ccnet_peer_to_string (processor->session->myself); + + ccnet_processor_send_response (processor, "200", "OK", + str->str, str->len+1); + ccnet_processor_done (processor, TRUE); + + g_string_free (str, TRUE); +} + + +static int put_pubinfo_start (CcnetProcessor *processor, int argc, char **argv) +{ + put_pubinfo (processor); + return 0; +} + + +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + return; +} diff --git a/net/common/processors/putpubinfo-proc.h b/net/common/processors/putpubinfo-proc.h new file mode 100644 index 0000000..82bf8e4 --- /dev/null +++ b/net/common/processors/putpubinfo-proc.h @@ -0,0 +1,29 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_PUTPUBINFO_PROC_H +#define CCNET_PUTPUBINFO_PROC_H + +#include +#include "processor.h" + +#define CCNET_TYPE_PUTPUBINFO_PROC (ccnet_putpubinfo_proc_get_type ()) +#define CCNET_PUTPUBINFO_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_PUTPUBINFO_PROC, CcnetPutpubinfoProc)) +#define CCNET_IS_PUTPUBINFO_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_PUTPUBINFO_PROC)) +#define CCNET_PUTPUBINFO_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_PUTPUBINFO_PROC, CcnetPutpubinfoProcClass)) +#define CCNET_IS_PUTPUBINFO_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_PUTPUBINFO_PROC)) +#define CCNET_PUTPUBINFO_PROC_PUT_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_PUTPUBINFO_PROC, CcnetPutpubinfoProcClass)) + +typedef struct _CcnetPutpubinfoProc CcnetPutpubinfoProc; +typedef struct _CcnetPutpubinfoProcClass CcnetPutpubinfoProcClass; + +struct _CcnetPutpubinfoProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetPutpubinfoProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_putpubinfo_proc_get_type (); + +#endif diff --git a/net/common/processors/rcvcmd-proc.c b/net/common/processors/rcvcmd-proc.c new file mode 100644 index 0000000..8cec256 --- /dev/null +++ b/net/common/processors/rcvcmd-proc.c @@ -0,0 +1,1142 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include +#include +#include + +#include "rcvcmd-proc.h" + +#include "peer.h" +#include "session.h" +#include "peer-mgr.h" +#include "connect-mgr.h" +#include "proc-factory.h" +#include "message.h" +#include "message-manager.h" + +#include "algorithms.h" +#include "utils.h" +#include "net.h" +#include "log.h" + +#ifdef CCNET_CLUSTER + #include "cluster-mgr.h" +#endif +#ifdef CCNET_DAEMON + #include "daemon-session.h" +#endif + + +#define SC_UNKNONW_CMD "400" +#define SS_UNKNONW_CMD "Unknown Command" +#define SC_BAD_PACKET_FMT "401" +#define SS_BAD_PACKET_FMT "Bad Packet Format" +#define SC_BAD_CMD_FMT "402" +#define SS_BAD_CMD_FMT "Bad Command Format" +#define SC_NO_MSG "403" +#define SS_NO_MSG "Message Not Exist" +#define SC_NO_PEER "410" +#define SS_NO_PEER "No Such Peer" +#define SC_NO_PROCESSOR "411" +#define SS_NO_PROCESSOR "No Such Processor" +#define SC_NO_USER "413" +#define SS_NO_USER "No Such User" +#define SC_NO_GROUP "430" +#define SS_NO_GROUP "No Such Group" + +#define PARSE_OPTIONS \ + do { \ + context = g_option_context_new (NULL); \ + g_option_context_add_main_entries (context, cmd_entries, NULL); \ + if (!g_option_context_parse (context, &argc, &argv, &error)) { \ + g_print ("option parsing failed: %s\n", error->message); \ + return -1; \ + } \ + } while (0) + +enum { + INIT +}; + +typedef struct { + int persist : 1; +} CcnetRcvcmdProcPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_RCVCMD_PROC, CcnetRcvcmdProcPriv)) + + +static int rcv_cmd_start (CcnetProcessor *processor, int argc, char **argv); +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + + +G_DEFINE_TYPE (CcnetRcvcmdProc, ccnet_rcvcmd_proc, CCNET_TYPE_PROCESSOR) + +static void +ccnet_rcvcmd_proc_class_init (CcnetRcvcmdProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->name = "rcvcmd-proc"; + proc_class->start = rcv_cmd_start; + proc_class->handle_update = handle_update; + + g_type_class_add_private (klass, sizeof (CcnetRcvcmdProcPriv)); +} + +static void +ccnet_rcvcmd_proc_init (CcnetRcvcmdProc *processor) +{ +} + + +static int rcv_cmd_start (CcnetProcessor *processor, int argc, char **argv) +{ + CcnetRcvcmdProcPriv *priv = GET_PRIV (processor); + + priv->persist = 0; + + while (0 < argc && argv[0][0] == '-') { + switch (argv[0][1]) { + case 'p': + priv->persist = 1; + break; + default: + break; + } + argc--; argv++; + } + + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + return 0; +} + +struct cmd +{ + char *name; + int (*handler) (CcnetProcessor *, int argc, char **argv); +}; + +static int shutdown_session (CcnetProcessor *, int, char **); +static int register_service (CcnetProcessor *, int, char **); +static int echo (CcnetProcessor *, int, char **); +static int list_proc (CcnetProcessor *, int, char **); +static int set_timeout (CcnetProcessor *, int, char **); + +static int add_role (CcnetProcessor *, int, char **); +static int set_addr (CcnetProcessor *, int, char **); +static int add_peer (CcnetProcessor *, int, char **); +static int delete_peer (CcnetProcessor *, int, char **); +static int delete_role (CcnetProcessor *, int, char **); +static int connect_peer (CcnetProcessor *, int, char **); +static int disconnect_peer (CcnetProcessor *, int, char **); +static int conn_cancel (CcnetProcessor *, int, char **); +static int invoke_echo (CcnetProcessor *, int, char **); + + +#ifdef CCNET_CLUSTER +static int add_member (CcnetProcessor *, int, char **); +static int add_master (CcnetProcessor *, int, char **); +static int redirect_peer (CcnetProcessor *, int, char **); +#endif + +#ifdef CCNET_DAEMON +static int add_relay (CcnetProcessor *, int, char **); +#endif + + +static struct cmd cmdtab[] = { + { "shutdown", shutdown_session }, + { "register-service", register_service }, + { "echo", echo }, + { "list-proc", list_proc }, + { "set-timeout", set_timeout }, + { "add-role", add_role }, + { "add-peer", add_peer }, + { "del-peer", delete_peer }, + { "del-role", delete_role }, + { "set-addr", set_addr }, +#ifdef CCNET_DAEMON + { "add-relay", add_relay }, +#endif +#ifdef CCNET_CLUSTER + { "add-member", add_member }, + { "add-master", add_master }, + { "redirect-peer", redirect_peer }, +#endif + /* { "group-create", create_group }, */ + /* { "group-follow", follow_group }, */ + /* { "group-unfollow", unfollow_group }, */ + /* { "group-show", show_group }, */ + /* { "group-add-member", add_group_member }, */ + /* { "group-del-member", delete_group_member }, */ + /* { "group-del-follower", delete_group_follower }, */ + /* { "group-add-maintainer", add_group_maintainer }, */ + { "connect", connect_peer }, + { "disconnect", disconnect_peer }, + { "conn-cancel", conn_cancel }, + { "invoke-echo", invoke_echo }, + { 0 }, +}; + +struct cmd * +getcmd (char *name) +{ + char *p, *q; + struct cmd *c, *found; + int nmatches, longest; + + longest = 0; + nmatches = 0; + found = 0; + for (c = cmdtab; (p = c->name); c++) { + for (q = name; *q == *p++; q++) + if (*q == 0) /* exact match? */ + return c; + if (!*q) { /* the name was a prefix */ + if (q - name > longest) { + longest = q - name; + nmatches = 1; + found = c; + } else if (q - name == longest) + nmatches++; + } + } + + if (nmatches > 1) + return (struct cmd *)-1; + return found; +} + + +static void handle_command (CcnetProcessor *processor, char *line) +{ + gchar **commands; + gchar **pcmd; + struct cmd *c; + int i; + + commands = g_strsplit_set (line, " \t", 10); + for (i = 0, pcmd = commands; *pcmd; pcmd++) + i++; + + if (i == 0) { + g_strfreev (commands); + return; + } + + /* puts (commands[0]); */ + c = getcmd (commands[0]); + if (c == NULL) { + ccnet_processor_send_response (processor, SC_UNKNONW_CMD, + SS_UNKNONW_CMD, NULL, 0); + } else + c->handler (processor, i, commands); + + g_strfreev (commands); +} + + +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + CcnetRcvcmdProcPriv *priv = GET_PRIV (processor); + + if (content[clen-1] != '\0') { + ccnet_processor_send_response (processor, SC_BAD_PACKET_FMT, + SS_BAD_PACKET_FMT, NULL, 0); + if (!priv->persist) + ccnet_processor_done (processor, FALSE); + return; + } + + handle_command (processor, content); + if (!priv->persist) + ccnet_processor_done (processor, TRUE); +} + +int shutdown_session (CcnetProcessor *processor, int argc, char **argv) +{ + argc--; + argv++; + + ccnet_processor_send_response (processor, SC_OK, NULL, NULL, 0); + + ccnet_session_on_exit (processor->session); + + exit (0); + + return 0; +} + + +static int register_service (CcnetProcessor *processor, + int argc, char **argv) +{ + argc--; + argv++; + + int ret = 0; + char *group = "inner"; + if (argc < 1) { + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + SS_BAD_CMD_FMT, NULL, 0); + return -1; + } + if (argc >= 2) + group = argv[1]; + ret = ccnet_session_register_service (processor->session, + argv[0], group, + processor->peer); + + if (ret < 0) + ccnet_processor_send_response (processor, SC_SERV_EXISTED, + SS_SERV_EXISTED, NULL, 0); + else + ccnet_processor_send_response (processor, SC_OK, + NULL, NULL, 0); + return 0; +} + + +static int echo (CcnetProcessor *processor, + int argc, char **argv) +{ + argc--; + argv++; + + if (argc == 1) { + ccnet_processor_send_response (processor, SC_OK, "", + argv[0], strlen(argv[0])+1); + + } else { + ccnet_processor_send_response (processor, SC_OK, "", + "Hello", strlen("Hello")+1); + } + + return 0; +} + +static int +list_proc (CcnetProcessor *processor, int argc, char **argv) +{ + argc--; + argv++; + + CcnetPeerManager *peerMgr; + CcnetPeer *peer; + CcnetProcessor *proc; + GList *peerlist, *peeriter; + GList *proclist, *prociter; + GString *buf = g_string_new (NULL); + + peerMgr = processor->session->peer_mgr; + peerlist = ccnet_peer_manager_get_peer_list (peerMgr); + + for (peeriter=peerlist; peeriter; peeriter=peeriter->next) { + peer = (CcnetPeer *)(peeriter->data); + proclist = g_hash_table_get_values (peer->processors); + + for (prociter=proclist; prociter; prociter = prociter->next) { + proc = (CcnetProcessor *)(prociter->data); + g_string_append_printf (buf, "%d\t%s\n", PRINT_ID(proc->id), + GET_PNAME(proc)); + } + + g_list_free (proclist); + } + g_list_free (peerlist); + + ccnet_processor_send_response (processor, SC_OK, SS_OK, + buf->str, buf->len); + + g_string_free (buf, TRUE); + + return 0; +} + +static int +set_timeout (CcnetProcessor *processor, int argc, char **argv) +{ + argc--; + argv++; + + if (argc < 2) { + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + SS_BAD_CMD_FMT, NULL, 0); + return -1; + } + + if (strcmp("proc-keepalive-timeout", argv[0]) == 0) { + ccnet_proc_factory_set_keepalive_timeout ( + processor->session->proc_factory, atoi(argv[1])); + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + return 0; + } + + ccnet_processor_send_response (processor, "401", "No such timer", NULL, 0); + return -1; +} + + +static int +set_addr (CcnetProcessor *processor, int argc, char **argv) +{ + CcnetPeerManager *mgr = processor->session->peer_mgr; + CcnetPeer *peer; + int port = DEFAULT_PORT; + argc--; + argv++; + + if (argc < 2) { + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + SS_BAD_CMD_FMT, NULL, 0); + return -1; + } + if (argc > 2) { + port = atoi (argv[2]); + if (port <= 0) { + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + "Invalid port", NULL, 0); + return -1; + } + } + + peer = ccnet_peer_manager_get_peer (mgr, argv[0]); + if (!peer) { + ccnet_processor_send_response (processor, "400", + "Load peer error", NULL, 0); + return -1; + } + + ccnet_peer_manager_set_peer_public_addr (mgr, peer, argv[1], port); + ccnet_processor_send_response (processor, SC_OK, SS_OK, + peer->id, 41); + g_object_unref (peer); + return 0; +} + +#ifdef CCNET_DAEMON + +/* add-relay [--id ] [--addr ] + */ +static int +add_relay (CcnetProcessor *processor, int argc, char **argv) +{ + CcnetPeerManager *mgr = processor->session->peer_mgr; + CcnetPeer *peer = NULL; + char *peer_id = NULL; + char *addr_port = NULL; + char *role = NULL; + char *addr; + uint16_t port; + int ret; + + GOptionContext *context; + GError *error = NULL; + GOptionEntry cmd_entries[] = { + { .long_name = "id", + .short_name = 0, + .flags = 0, + .arg = G_OPTION_ARG_STRING, + .arg_data = &peer_id, + .description = "the peer id", + .arg_description = NULL }, + { .long_name = "addr", + .short_name = 0, + .flags = 0, + .arg = G_OPTION_ARG_STRING, + .arg_data = &addr_port, + .description = "the address and port of the peer", + .arg_description = NULL }, + { NULL }, + }; + + PARSE_OPTIONS; + + /* check addr_port and peer id */ + if (addr_port) { + addr = addr_port; + char *p; + if ( (p = strchr(addr_port, ':')) == NULL) { + port = DEFAULT_PORT; + } else { + *p = '\0'; + port = atoi(p+1); + if (port == 0) { + ccnet_processor_send_response ( + processor, "400", "Invalid Address", NULL, 0); + ret = -1; + goto out; + } + } + } + + if (peer_id) { + if (!peer_id_valid(peer_id)) { + ccnet_processor_send_response ( + processor, "400", "Invalid Peer ID", NULL, 0); + ret = -1; + goto out; + } + } + + if (addr_port && peer_id) { + peer = ccnet_peer_manager_get_peer (mgr, peer_id); + if (!peer) { + ccnet_message("Add server %.8s %s:%d\n", peer_id, addr, port); + peer = ccnet_peer_new (peer_id); + ccnet_peer_manager_add_peer (mgr, peer); + ccnet_peer_manager_set_peer_public_addr (mgr, peer, addr, port); + ccnet_peer_manager_add_role (mgr, peer, "MyRelay"); + ccnet_conn_manager_connect_peer (processor->session->connMgr, peer); + } + + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + ret = 0; + goto out; + } + + /* only addr */ + if (addr_port) { + peer = ccnet_peer_manager_add_resolve_peer ( + processor->session->peer_mgr, addr, port); + peer->want_tobe_relay = 1; + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + ret = 0; + goto out; + } + + /* only id */ + if (peer_id) { + peer = ccnet_peer_manager_get_peer (mgr, peer_id); + if (!peer) { + peer = ccnet_peer_new (peer_id); + ccnet_peer_manager_add_peer (mgr, peer); + } + + ccnet_peer_manager_add_role (mgr, peer, "MyRelay"); + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + ret = 0; + goto out; + } + + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + SS_BAD_CMD_FMT, NULL, 0); + ret = -1; + +out: + g_free (addr_port); + g_free (role); + g_free (peer_id); + if (peer) g_object_unref (peer); + return ret; +} + + +#endif + + +static int +add_role (CcnetProcessor *processor, int argc, char **argv) +{ + argc--; + argv++; + + CcnetPeerManager *mgr = processor->session->peer_mgr; + CcnetPeer *peer; + + if (argc != 2) { + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + SS_BAD_CMD_FMT, NULL, 0); + return -1; + } + + if (strlen(argv[0]) != 40) { + ccnet_processor_send_response (processor, "400", + "Peer id must be of length 40", NULL, 0); + return -1; + } + + peer = ccnet_peer_manager_get_peer (mgr, argv[0]); + if (!peer) { + ccnet_processor_send_response (processor, SC_NO_PEER, + SS_NO_PEER, NULL, 0); + return -1; + } + + ccnet_peer_manager_add_role (mgr, peer, argv[1]); + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + + g_object_unref (peer); + return 0; +} + + +static int +delete_role (CcnetProcessor *processor, int argc, char **argv) +{ + argc--; + argv++; + + CcnetPeerManager *mgr = processor->session->peer_mgr; + CcnetPeer *peer; + + if (argc != 2) { + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + SS_BAD_CMD_FMT, NULL, 0); + return -1; + } + + if (strlen(argv[0]) != 40) { + ccnet_processor_send_response (processor, "400", + "Peer id must be of length 40", NULL, 0); + return -1; + } + + peer = ccnet_peer_manager_get_peer (mgr, argv[0]); + if (!peer) { + ccnet_processor_send_response (processor, SC_NO_PEER, + SS_NO_PEER, NULL, 0); + return -1; + } + + ccnet_peer_manager_remove_role (mgr, peer, argv[1]); + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + + g_object_unref (peer); + return 0; +} + + +/* add-peer [--id ] [--addr ] [--role ] + */ +static int +add_peer (CcnetProcessor *processor, int argc, char **argv) +{ + CcnetPeerManager *mgr = processor->session->peer_mgr; + CcnetPeer *peer = NULL; + char *peer_id = NULL; + char *addr_port = NULL; + char *role = NULL; + char *addr; + uint16_t port; + int ret; + + GOptionContext *context; + GError *error = NULL; + GOptionEntry cmd_entries[] = { + { .long_name = "id", + .short_name = 0, + .flags = 0, + .arg = G_OPTION_ARG_STRING, + .arg_data = &peer_id, + .description = "the peer id", + .arg_description = NULL }, + { .long_name = "addr", + .short_name = 0, + .flags = 0, + .arg = G_OPTION_ARG_STRING, + .arg_data = &addr_port, + .description = "the address and port of the peer", + .arg_description = NULL }, + { .long_name = "role", + .short_name = 0, + .flags = 0, + .arg = G_OPTION_ARG_STRING, + .arg_data = &role, + .description = "the role of the peer", + .arg_description = NULL }, + { NULL }, + }; + + PARSE_OPTIONS; + + /* check addr_port and peer id */ + if (addr_port) { + addr = addr_port; + char *p; + if ( (p = strchr(addr_port, ':')) == NULL) { + port = DEFAULT_PORT; + } else { + *p = '\0'; + port = atoi(p+1); + if (port == 0) { + ccnet_processor_send_response ( + processor, "400", "Invalid Address", NULL, 0); + ret = -1; + goto out; + } + } + } + + if (peer_id) { + if (!peer_id_valid(peer_id)) { + ccnet_processor_send_response ( + processor, "400", "Invalid Peer ID", NULL, 0); + ret = -1; + goto out; + } + } + + if (addr_port && peer_id) { + peer = ccnet_peer_manager_get_peer (mgr, peer_id); + if (!peer) { + peer = ccnet_peer_new (peer_id); + ccnet_peer_manager_add_peer (mgr, peer); + } + + ccnet_peer_manager_set_peer_public_addr (mgr, peer, addr, port); + if (role) + ccnet_peer_manager_add_role (mgr, peer, role); + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + ret = 0; + goto out; + } + + /* only addr */ + if (addr_port) { + peer = ccnet_peer_manager_add_resolve_peer ( + processor->session->peer_mgr, addr, port); + g_free (peer->intend_role); + peer->intend_role = g_strdup(role); + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + ret = 0; + goto out; + } + + /* only id */ + if (peer_id) { + peer = ccnet_peer_manager_get_peer (mgr, peer_id); + if (peer) { + ret = 0; + goto out; + } + + peer = ccnet_peer_new (peer_id); + ccnet_peer_manager_add_peer (mgr, peer); + if (role) + ccnet_peer_manager_add_role (mgr, peer, role); + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + ret = 0; + goto out; + } + + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + SS_BAD_CMD_FMT, NULL, 0); + ret = -1; + +out: + g_free (addr_port); + g_free (role); + g_free (peer_id); + if (peer) g_object_unref (peer); + return ret; +} + + +static int +delete_peer (CcnetProcessor *processor, int argc, char **argv) +{ + argc--; + argv++; + + CcnetPeerManager *mgr = processor->session->peer_mgr; + CcnetPeer *peer; + + if (argc < 1) { + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + SS_BAD_CMD_FMT, NULL, 0); + return -1; + } + + if (strlen(argv[0]) != 40) { + ccnet_processor_send_response (processor, "400", + "Peer id must be of length 40", NULL, 0); + return -1; + } + + peer = ccnet_peer_manager_get_peer (mgr, argv[0]); + if (peer) { + ccnet_peer_manager_remove_peer (mgr, peer); + g_object_unref (peer); + } + + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + return 0; +} + + +static int +connect_peer (CcnetProcessor *processor, int argc, char **argv) +{ + argc--; + argv++; + + CcnetPeerManager *mgr = processor->session->peer_mgr; + char *peer_id; + CcnetPeer *peer; + + if (argc != 1) { + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + SS_BAD_CMD_FMT, NULL, 0); + return -1; + } + + if (strlen(argv[0]) != 40) { + ccnet_processor_send_response (processor, "400", + "Peer id must be of length 40", NULL, 0); + return -1; + } + peer_id = argv[0]; + + peer = ccnet_peer_manager_get_peer (mgr, peer_id); + if (!peer) { + ccnet_processor_send_response ( + processor, "400", "Can not find peer", NULL, 0); + return -1; + } + + ccnet_conn_manager_add_to_conn_list (processor->session->connMgr, peer); + ccnet_conn_manager_connect_peer (processor->session->connMgr, peer); + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + + g_object_unref (peer); + return 0; +} + +static int +disconnect_peer (CcnetProcessor *processor, int argc, char **argv) +{ + argc--; + argv++; + + CcnetPeerManager *mgr = processor->session->peer_mgr; + char *peer_id; + CcnetPeer *peer; + + if (argc != 1) { + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + SS_BAD_CMD_FMT, NULL, 0); + return -1; + } + + if (strlen(argv[0]) != 40) { + ccnet_processor_send_response (processor, "400", + "Peer id must be of length 40", NULL, 0); + return -1; + } + peer_id = argv[0]; + + peer = ccnet_peer_manager_get_peer (mgr, peer_id); + if (!peer) { + ccnet_processor_send_response ( + processor, "400", "Can not find peer", NULL, 0); + return -1; + } + + ccnet_conn_manager_remove_from_conn_list (processor->session->connMgr, + peer); + ccnet_peer_shutdown (peer); + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + + g_object_unref (peer); + return 0; +} + + +static int +conn_cancel (CcnetProcessor *processor, int argc, char **argv) +{ + argc--; + argv++; + + if (argc != 2) { + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + SS_BAD_CMD_FMT, NULL, 0); + return -1; + } + + int port = atoi(argv[1]); + + ccnet_conn_manager_cancel_conn (processor->session->connMgr, argv[0], port); + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + + return 0; +} + + +static int +invoke_echo (CcnetProcessor *processor, int argc, char **argv) +{ + argc--; + argv++; + + CcnetPeerManager *mgr = processor->session->peer_mgr; + CcnetPeer *peer; + + if (argc != 1) { + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + SS_BAD_CMD_FMT, NULL, 0); + return -1; + } + + if (!peer_id_valid(argv[0])) { + ccnet_processor_send_response (processor, "400", + "Invalid peer id", NULL, 0); + return -1; + } + + peer = ccnet_peer_manager_get_peer (mgr, argv[0]); + if (!peer) { + ccnet_processor_send_response ( + processor, "400", "Can not find peer", NULL, 0); + return -1; + } + CcnetProcessor *proc; + proc = ccnet_proc_factory_create_master_processor ( + processor->session->proc_factory, "echo", peer); + ccnet_processor_start (proc, 0, NULL); + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + + g_object_unref (peer); + return 0; +} + + +#ifdef CCNET_CLUSTER + +extern CcnetClusterManager *cluster_mgr; + +/* add-member [--id ] + */ +static int +add_member (CcnetProcessor *processor, int argc, char **argv) +{ + CcnetPeerManager *mgr = processor->session->peer_mgr; + CcnetPeer *peer = NULL; + char *peer_id = NULL; + char *addr_port = NULL; + char *addr; + uint16_t port; + int ret; + + GOptionContext *context; + GError *error = NULL; + GOptionEntry cmd_entries[] = { + { .long_name = "id", + .short_name = 0, + .flags = 0, + .arg = G_OPTION_ARG_STRING, + .arg_data = &peer_id, + .description = "the peer id", + .arg_description = NULL }, + { .long_name = "addr", + .short_name = 0, + .flags = 0, + .arg = G_OPTION_ARG_STRING, + .arg_data = &addr_port, + .description = "the address and port of the peer", + .arg_description = NULL }, + { NULL }, + }; + + PARSE_OPTIONS; + + + if (!peer_id) { + ccnet_processor_send_response ( + processor, "400", "Must specify Peer ID", NULL, 0); + ret = -1; + goto out; + } + + if (!peer_id_valid(peer_id)) { + ccnet_processor_send_response ( + processor, "400", "Invalid Peer ID", NULL, 0); + ret = -1; + goto out; + } + + /* check addr_port and peer id */ + if (addr_port) { + addr = addr_port; + char *p; + if ( (p = strchr(addr_port, ':')) == NULL) { + port = DEFAULT_PORT; + } else { + *p = '\0'; + port = atoi(p+1); + if (port == 0) { + ccnet_processor_send_response ( + processor, "400", "Invalid Address", NULL, 0); + ret = -1; + goto out; + } + } + } + + peer = ccnet_peer_manager_get_peer (mgr, peer_id); + if (!peer) { + peer = ccnet_peer_new (peer_id); + ccnet_peer_manager_add_peer (mgr, peer); + } + if (addr) + ccnet_peer_manager_set_peer_public_addr (mgr, peer, addr, port); + + ccnet_peer_manager_add_role (mgr, peer, "ClusterMember"); + ccnet_cluster_manager_add_member (cluster_mgr, peer); + + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + ret = 0; + goto out; + +out: + g_free (peer_id); + if (peer) g_object_unref (peer); + return ret; +} + +/* add-master [--id ] [--addr ] + */ +static int +add_master (CcnetProcessor *processor, int argc, char **argv) +{ + CcnetPeerManager *mgr = processor->session->peer_mgr; + CcnetPeer *peer = NULL; + char *peer_id = NULL; + char *addr_port = NULL; + char *addr; + uint16_t port; + int ret; + + GOptionContext *context; + GError *error = NULL; + GOptionEntry cmd_entries[] = { + { .long_name = "id", + .short_name = 0, + .flags = 0, + .arg = G_OPTION_ARG_STRING, + .arg_data = &peer_id, + .description = "the peer id", + .arg_description = NULL }, + { .long_name = "addr", + .short_name = 0, + .flags = 0, + .arg = G_OPTION_ARG_STRING, + .arg_data = &addr_port, + .description = "the address and port of the peer", + .arg_description = NULL }, + { NULL }, + }; + + PARSE_OPTIONS; + + + if (!peer_id) { + ccnet_processor_send_response ( + processor, "400", "Must specify Peer ID", NULL, 0); + ret = -1; + goto out; + } + + if (!peer_id_valid(peer_id)) { + ccnet_processor_send_response ( + processor, "400", "Invalid Peer ID", NULL, 0); + ret = -1; + goto out; + } + + /* check addr_port and peer id */ + if (addr_port) { + addr = addr_port; + char *p; + if ( (p = strchr(addr_port, ':')) == NULL) { + port = DEFAULT_PORT; + } else { + *p = '\0'; + port = atoi(p+1); + if (port == 0) { + ccnet_processor_send_response ( + processor, "400", "Invalid Address", NULL, 0); + ret = -1; + goto out; + } + } + } + + peer = ccnet_peer_manager_get_peer (mgr, peer_id); + if (!peer) { + peer = ccnet_peer_new (peer_id); + ccnet_peer_manager_add_peer (mgr, peer); + } + + if (addr) + ccnet_peer_manager_set_peer_public_addr (mgr, peer, addr, port); + + ccnet_peer_manager_add_role (mgr, peer, "ClusterMaster"); + ccnet_cluster_manager_add_master (cluster_mgr, peer); + + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + ret = 0; + goto out; + +out: + g_free (addr); + g_free (peer_id); + if (peer) g_object_unref (peer); + return ret; +} + +static int +redirect_peer (CcnetProcessor *processor, int argc, char **argv) +{ + argc--; + argv++; + + CcnetPeerManager *mgr = processor->session->peer_mgr; + char *peer_id, *to_id; + CcnetPeer *peer, *to; + + if (argc != 2) { + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + SS_BAD_CMD_FMT, NULL, 0); + return -1; + } + + if (!peer_id_valid(argv[0]) || !peer_id_valid(argv[1])) { + ccnet_processor_send_response (processor, "400", + "Invalid peer id", NULL, 0); + return -1; + } + + peer_id = argv[0]; + to_id = argv[1]; + peer = ccnet_peer_manager_get_peer (mgr, peer_id); + if (!peer) { + ccnet_processor_send_response ( + processor, "400", "Can not find peer", NULL, 0); + return -1; + } + to = ccnet_peer_manager_get_peer (mgr, to_id); + if (!to) { + g_object_unref (peer); + ccnet_processor_send_response ( + processor, "400", "Can not redirect destination peer", NULL, 0); + return -1; + } + + ccnet_peer_manager_redirect_peer (mgr, peer, to); + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + + g_object_unref (peer); + g_object_unref (to); + return 0; +} + +#endif diff --git a/net/common/processors/rcvcmd-proc.h b/net/common/processors/rcvcmd-proc.h new file mode 100644 index 0000000..30720d2 --- /dev/null +++ b/net/common/processors/rcvcmd-proc.h @@ -0,0 +1,29 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_RCVCMD_PROC_H +#define CCNET_RCVCMD_PROC_H + +#include +#include "processor.h" + +#define CCNET_TYPE_RCVCMD_PROC (ccnet_rcvcmd_proc_get_type ()) +#define CCNET_RCVCMD_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_RCVCMD_PROC, CcnetRcvcmdProc)) +#define CCNET_IS_RCVCMD_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_RCVCMD_PROC)) +#define CCNET_RCVCMD_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_RCVCMD_PROC, CcnetRcvcmdProcClass)) +#define CCNET_IS_RCVCMD_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_RCVCMD_PROC)) +#define CCNET_RCVCMD_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_RCVCMD_PROC, CcnetRcvcmdProcClass)) + +typedef struct _CcnetRcvcmdProc CcnetRcvcmdProc; +typedef struct _CcnetRcvcmdProcClass CcnetRcvcmdProcClass; + +struct _CcnetRcvcmdProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetRcvcmdProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_rcvcmd_proc_get_type (); + +#endif diff --git a/net/common/processors/rcvmsg-proc.c b/net/common/processors/rcvmsg-proc.c new file mode 100644 index 0000000..13e9b5a --- /dev/null +++ b/net/common/processors/rcvmsg-proc.c @@ -0,0 +1,83 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "peer.h" +#include "message.h" +#include "session.h" +#include "message-manager.h" +#include "peer-mgr.h" +#include "rcvmsg-proc.h" +#include "algorithms.h" + +#define DEBUG_FLAG CCNET_DEBUG_MESSAGE +#include "log.h" + +static int rcv_msg_start (CcnetProcessor *processor, int argc, char **argv); +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + + +G_DEFINE_TYPE (CcnetRcvmsgProc, ccnet_rcvmsg_proc, CCNET_TYPE_PROCESSOR) + +static void +ccnet_rcvmsg_proc_class_init (CcnetRcvmsgProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->name = "rcvmsg-proc"; + proc_class->start = rcv_msg_start; + proc_class->handle_update = handle_update; +} + +static void +ccnet_rcvmsg_proc_init (CcnetRcvmsgProc *processor) +{ +} + +static int rcv_msg_start (CcnetProcessor *processor, int argc, char **argv) +{ + ccnet_processor_send_response (processor, "200", "OK", NULL, 0); + return 0; +} + +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + CcnetMessage *msg; + + if (processor->peer->is_local) { + msg = ccnet_message_from_string_local (content, clen); + ccnet_send_message (processor->session, msg); + ccnet_message_unref (msg); + } else { + msg = ccnet_message_from_string (content, clen); + if (!msg) { + g_warning ("Wrong message format.\n"); + ccnet_processor_done (processor, FALSE); + return; + } + + msg->rtime = time(NULL); + ccnet_debug ("[msg] Received a message : %s - %.10s\n", + msg->app, msg->body); + + int ret = ccnet_recv_message (processor->session, msg); + if (ret == -1) { + ccnet_message ("[msg] Message from %.8s permission error\n", + msg->from); + ccnet_processor_send_response (processor, SC_PERM_ERR, + SS_PERM_ERR, NULL, 0); + ccnet_processor_done (processor, TRUE); + ccnet_message_unref (msg); + return; + } + + ccnet_message_unref (msg); + } + + ccnet_processor_send_response (processor, "200", "OK", NULL, 0); + ccnet_processor_done (processor, TRUE); +} diff --git a/net/common/processors/rcvmsg-proc.h b/net/common/processors/rcvmsg-proc.h new file mode 100644 index 0000000..e5dafc2 --- /dev/null +++ b/net/common/processors/rcvmsg-proc.h @@ -0,0 +1,29 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_RCVMSG_PROC_H +#define CCNET_RCVMSG_PROC_H + +#include +#include "processor.h" + +#define CCNET_TYPE_RCVMSG_PROC (ccnet_rcvmsg_proc_get_type ()) +#define CCNET_RCVMSG_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_RCVMSG_PROC, CcnetRcvmsgProc)) +#define CCNET_IS_RCVMSG_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_RCVMSG_PROC)) +#define CCNET_RCVMSG_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_RCVMSG_PROC, CcnetRcvmsgProcClass)) +#define CCNET_IS_RCVMSG_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_RCVMSG_PROC)) +#define CCNET_RCVMSG_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_RCVMSG_PROC, CcnetRcvmsgProcClass)) + +typedef struct _CcnetRcvmsgProc CcnetRcvmsgProc; +typedef struct _CcnetRcvmsgProcClass CcnetRcvmsgProcClass; + +struct _CcnetRcvmsgProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetRcvmsgProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_rcvmsg_proc_get_type (); + +#endif diff --git a/net/common/processors/recvsessionkey-proc.c b/net/common/processors/recvsessionkey-proc.c new file mode 100644 index 0000000..ce46d66 --- /dev/null +++ b/net/common/processors/recvsessionkey-proc.c @@ -0,0 +1,155 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "session.h" +#include "common.h" +#include "processor.h" +#include "peer.h" +#include "peer-mgr.h" +#include "log.h" +#include "rsa.h" + +#include "recvsessionkey-proc.h" + +extern CcnetSession *session; + +#define SC_SESSION_KEY "300" +#define SS_SESSION_KEY "session key" +#define SC_ALREADY_HAS_KEY "301" +#define SS_ALREADY_HAS_KEY "already has your session key" +#define SC_BAD_KEY "302" +#define SS_BAD_KEY "bad session key" + +G_DEFINE_TYPE (CcnetRecvsessionkeyProc, ccnet_recvsessionkey_proc, CCNET_TYPE_PROCESSOR) + +static int start (CcnetProcessor *processor, int argc, char **argv); +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void +release_resource(CcnetProcessor *processor) +{ + CCNET_PROCESSOR_CLASS (ccnet_recvsessionkey_proc_parent_class)->release_resource (processor); +} + + +static void +ccnet_recvsessionkey_proc_class_init (CcnetRecvsessionkeyProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->name = "receive-session-key"; + proc_class->start = start; + proc_class->handle_update = handle_update; + proc_class->release_resource = release_resource; +} + +static void +ccnet_recvsessionkey_proc_init (CcnetRecvsessionkeyProc *processor) +{ +} + + +static int +start (CcnetProcessor *processor, int argc, char **argv) +{ + if (argc != 0) { + ccnet_processor_send_response (processor, SC_BAD_ARGS, SS_BAD_ARGS, NULL, 0); + ccnet_processor_done (processor, FALSE); + return -1; + } + + if (processor->peer->session_key) { + ccnet_processor_send_response (processor, + SC_ALREADY_HAS_KEY, + SS_ALREADY_HAS_KEY, + NULL, 0); + ccnet_processor_done (processor, FALSE); + return -1; + } + + ccnet_processor_send_response (processor, + SC_SESSION_KEY, SS_SESSION_KEY, + NULL, 0); + + return 0; +} + +static unsigned char * +decrypt_data (CcnetPeer *peer, const char *content, int clen, int *len_p) +{ + RSA *privkey = session->privkey; + unsigned char *buf; + + buf = private_key_decrypt(privkey, (unsigned char *)content, + clen, len_p); + if (*len_p <= 0) { + ccnet_warning ("failed to decrypt session key from peer %.10s", + peer->id); + g_free (buf); + buf = NULL; + } + + return buf; +} + +static gboolean +update_peer_session_key (CcnetPeer *peer, + const char *content, + int clen) +{ + char *buf; + int key_len = 0; + + if (peer->session_key) { + ccnet_warning ("[recv session key] peer %.10s already has a session key", + peer->id); + return FALSE; + } + + buf = (char *)decrypt_data (peer, content, clen, &key_len); + if (buf) { + peer->session_key = g_strndup(buf, key_len); + g_free (buf); + return TRUE; + } else { + ccnet_warning ("faied to decrypt session key"); + return FALSE; + } +} + +static void +handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + if (strcmp(code, SC_SESSION_KEY) == 0) { + if (processor->peer->session_key) { + ccnet_processor_send_response (processor, + SC_ALREADY_HAS_KEY, + SS_ALREADY_HAS_KEY, + NULL, 0); + ccnet_processor_done (processor, TRUE); + + } else if (update_peer_session_key (processor->peer, content, clen)) { + ccnet_processor_send_response (processor, + SC_OK, SS_OK, + NULL, 0); + + ccnet_peer_manager_on_peer_session_key_received (processor->peer->manager, + processor->peer); + + ccnet_processor_done (processor, TRUE); + } else { + ccnet_processor_send_response (processor, + SC_BAD_KEY, SS_BAD_KEY, + NULL, 0); + ccnet_processor_done (processor, FALSE); + } + + } else { + ccnet_warning ("[recv session key] bad update %s:%s\n", + code, code_msg); + ccnet_processor_done (processor, FALSE); + } +} diff --git a/net/common/processors/recvsessionkey-proc.h b/net/common/processors/recvsessionkey-proc.h new file mode 100644 index 0000000..6325228 --- /dev/null +++ b/net/common/processors/recvsessionkey-proc.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_RECVSESSIONKEY_PROC_H +#define CCNET_RECVSESSIONKEY_PROC_H + +#include + + +#define CCNET_TYPE_RECVSESSIONKEY_PROC (ccnet_recvsessionkey_proc_get_type ()) +#define CCNET_RECVSESSIONKEY_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_RECVSESSIONKEY_PROC, CcnetRecvsessionkeyProc)) +#define CCNET_IS_RECVSESSIONKEY_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_RECVSESSIONKEY_PROC)) +#define CCNET_RECVSESSIONKEY_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_RECVSESSIONKEY_PROC, CcnetRecvsessionkeyProcClass)) +#define IS_CCNET_RECVSESSIONKEY_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_RECVSESSIONKEY_PROC)) +#define CCNET_RECVSESSIONKEY_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_RECVSESSIONKEY_PROC, CcnetRecvsessionkeyProcClass)) + +typedef struct _CcnetRecvsessionkeyProc CcnetRecvsessionkeyProc; +typedef struct _CcnetRecvsessionkeyProcClass CcnetRecvsessionkeyProcClass; + +struct _CcnetRecvsessionkeyProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetRecvsessionkeyProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_recvsessionkey_proc_get_type (); + +#endif + diff --git a/net/common/processors/recvsessionkey-v2-proc.c b/net/common/processors/recvsessionkey-v2-proc.c new file mode 100644 index 0000000..ce0df4a --- /dev/null +++ b/net/common/processors/recvsessionkey-v2-proc.c @@ -0,0 +1,186 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "session.h" +#include "common.h" +#include "processor.h" +#include "peer.h" +#include "peer-mgr.h" +#include "log.h" +#include "rsa.h" + +#include "recvsessionkey-v2-proc.h" + +extern CcnetSession *session; + +#define SC_SESSION_KEY "300" +#define SS_SESSION_KEY "session key" +#define SC_ALREADY_HAS_KEY "301" +#define SS_ALREADY_HAS_KEY "already has your session key" +#define SC_NO_ENCRYPT "303" +#define SS_NO_ENCRYPT "Donot encrypt channel" +#define SC_BAD_KEY "400" +#define SS_BAD_KEY "bad session key" + + +typedef struct { + int encrypt_channel; +} CcnetRecvskey2ProcPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_RECVSKEY2_PROC, CcnetRecvskey2ProcPriv)) + +#define USE_PRIV \ + CcnetRecvskey2ProcPriv *priv = GET_PRIV(processor); + + +G_DEFINE_TYPE (CcnetRecvskey2Proc, ccnet_recvskey2_proc, CCNET_TYPE_PROCESSOR) + + + +static int start (CcnetProcessor *processor, int argc, char **argv); +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void +release_resource(CcnetProcessor *processor) +{ + CCNET_PROCESSOR_CLASS (ccnet_recvskey2_proc_parent_class)->release_resource (processor); +} + + +static void +ccnet_recvskey2_proc_class_init (CcnetRecvskey2ProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->name = "receive-skey2"; + proc_class->start = start; + proc_class->handle_update = handle_update; + proc_class->release_resource = release_resource; + + g_type_class_add_private (klass, sizeof (CcnetRecvskey2ProcPriv)); +} + +static void +ccnet_recvskey2_proc_init (CcnetRecvskey2Proc *processor) +{ +} + + +static int +start (CcnetProcessor *processor, int argc, char **argv) +{ + USE_PRIV; + + if (processor->peer->session_key) { + ccnet_processor_send_response (processor, + SC_ALREADY_HAS_KEY, + SS_ALREADY_HAS_KEY, + NULL, 0); + ccnet_processor_done (processor, FALSE); + return -1; + } + + if (argc == 1 && g_strcmp0(argv[0], "--enc-channel") == 0) + priv->encrypt_channel = 1; + else + priv->encrypt_channel = 0; + + ccnet_processor_send_response (processor, + SC_SESSION_KEY, SS_SESSION_KEY, + NULL, 0); + + return 0; +} + +static unsigned char * +decrypt_data (CcnetPeer *peer, const char *content, int clen, int *len_p) +{ + RSA *privkey = session->privkey; + unsigned char *buf; + + buf = private_key_decrypt(privkey, (unsigned char *)content, + clen, len_p); + if (*len_p <= 0) { + ccnet_warning ("failed to decrypt session key from peer %.10s", + peer->id); + g_free (buf); + buf = NULL; + } + + return buf; +} + +static gboolean +update_peer_session_key (CcnetPeer *peer, + const char *content, + int clen) +{ + char *buf; + int key_len = 0; + + buf = (char *)decrypt_data (peer, content, clen, &key_len); + if (buf) { + peer->session_key = g_strndup(buf, key_len); + g_free (buf); + return TRUE; + } else { + ccnet_warning ("faied to decrypt session key"); + return FALSE; + } +} + +static void +handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + USE_PRIV; + + if (strcmp(code, SC_SESSION_KEY) == 0) { + if (processor->peer->session_key) { + ccnet_processor_send_response (processor, + SC_ALREADY_HAS_KEY, + SS_ALREADY_HAS_KEY, + NULL, 0); + ccnet_processor_done (processor, TRUE); + return; + } + + if (update_peer_session_key (processor->peer, content, clen) < 0) { + ccnet_processor_send_response (processor, + SC_BAD_KEY, SS_BAD_KEY, + NULL, 0); + ccnet_processor_done (processor, FALSE); + return; + } + + if (priv->encrypt_channel) { + /* peer ask to encrypt channel, check whether we want it too */ + if (ccnet_session_should_encrypt_channel(processor->session)) { + /* send the ok reply first */ + ccnet_processor_send_response (processor, + SC_OK, SS_OK, + NULL, 0); + /* now setup encryption */ + if (ccnet_peer_prepare_channel_encryption (processor->peer) < 0) + /* this is very rare, we just print a warning */ + ccnet_warning ("Error in prepare channel encryption\n"); + } else + ccnet_processor_send_response ( + processor, SC_NO_ENCRYPT, SS_NO_ENCRYPT, NULL, 0); + } else + ccnet_processor_send_response ( + processor, SC_OK, SS_OK, NULL, 0); + + ccnet_peer_manager_on_peer_session_key_received (processor->peer->manager, + processor->peer); + ccnet_processor_done (processor, TRUE); + return; + } + + ccnet_warning ("[recv session key] bad update %s:%s\n", + code, code_msg); + ccnet_processor_done (processor, FALSE); +} diff --git a/net/common/processors/recvsessionkey-v2-proc.h b/net/common/processors/recvsessionkey-v2-proc.h new file mode 100644 index 0000000..74226fa --- /dev/null +++ b/net/common/processors/recvsessionkey-v2-proc.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_RECVSKEY2_PROC_H +#define CCNET_RECVSKEY2_PROC_H + +#include + + +#define CCNET_TYPE_RECVSKEY2_PROC (ccnet_recvskey2_proc_get_type ()) +#define CCNET_RECVSKEY2_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_RECVSKEY2_PROC, CcnetRecvskey2Proc)) +#define CCNET_IS_RECVSKEY2_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_RECVSKEY2_PROC)) +#define CCNET_RECVSKEY2_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_RECVSKEY2_PROC, CcnetRecvskey2ProcClass)) +#define IS_CCNET_RECVSKEY2_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_RECVSKEY2_PROC)) +#define CCNET_RECVSKEY2_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_RECVSKEY2_PROC, CcnetRecvskey2ProcClass)) + +typedef struct _CcnetRecvskey2Proc CcnetRecvskey2Proc; +typedef struct _CcnetRecvskey2ProcClass CcnetRecvskey2ProcClass; + +struct _CcnetRecvskey2Proc { + CcnetProcessor parent_instance; +}; + +struct _CcnetRecvskey2ProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_recvskey2_proc_get_type (); + +#endif + diff --git a/net/common/processors/rpcserver-proc.c b/net/common/processors/rpcserver-proc.c new file mode 100644 index 0000000..76e585c --- /dev/null +++ b/net/common/processors/rpcserver-proc.c @@ -0,0 +1,134 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include +#include "rpcserver-proc.h" +#include "rpc-common.h" + +#define DEBUG_FLAG CCNET_DEBUG_PEER +#include "log.h" + +typedef struct { + char *buf; + int len; + int off; + /* struct timeval start; */ +} CcnetRpcserverProcPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_RPCSERVER_PROC, CcnetRpcserverProcPriv)) + +G_DEFINE_TYPE (CcnetRpcserverProc, ccnet_rpcserver_proc, CCNET_TYPE_PROCESSOR) + +static int start (CcnetProcessor *processor, int argc, char **argv); +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void +release_resource(CcnetProcessor *processor) +{ + /* CcnetRpcserverProcPriv *priv = GET_PRIV (processor); */ + /* struct timeval end, intv; */ + + /* gettimeofday(&end, NULL); */ + /* timersub(&end, &priv->start, &intv); */ + /* fprintf (stdout, "[rpcserver] Time spend in proc: %ds %dus\n", */ + /* intv.tv_sec, intv.tv_usec); */ + + CCNET_PROCESSOR_CLASS (ccnet_rpcserver_proc_parent_class)->release_resource (processor); +} + + +static void +ccnet_rpcserver_proc_class_init (CcnetRpcserverProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->start = start; + proc_class->handle_update = handle_update; + proc_class->release_resource = release_resource; + proc_class->name = "rpcserver-proc"; + + g_type_class_add_private (klass, sizeof(CcnetRpcserverProcPriv)); +} + +static void +ccnet_rpcserver_proc_init (CcnetRpcserverProc *processor) +{ +} + + +static int +start (CcnetProcessor *processor, int argc, char **argv) +{ + /* CcnetRpcserverProcPriv *priv = GET_PRIV (processor); */ + /* gettimeofday(&priv->start, NULL); */ + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + + return 0; +} + + +static void +handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + CcnetRpcserverProcPriv *priv = GET_PRIV (processor); + + if (memcmp (code, SC_CLIENT_CALL, 3) == 0) { + gsize ret_len; + char *svc_name = processor->name; + char *ret = searpc_server_call_function (svc_name, content, clen, &ret_len); + + if (ret_len < MAX_TRANSFER_LENGTH) { + ccnet_processor_send_response ( + processor, SC_SERVER_RET, SS_SERVER_RET, ret, ret_len); + g_free (ret); + /* ccnet_processor_done (processor, TRUE); */ + return; + } + + /* we need to split data into multiple segments */ + priv->buf = ret; + priv->len = ret_len; + priv->off = 0; + + /* fprintf (stderr, "Send %d\n", MAX_TRANSFER_LENGTH); */ + ccnet_processor_send_response (processor, SC_SERVER_MORE, + SS_SERVER_MORE, priv->buf, + MAX_TRANSFER_LENGTH); + priv->off = MAX_TRANSFER_LENGTH; + + return; + } + + if (memcmp (code, SC_CLIENT_MORE, 3) == 0) { + if (priv->off + MAX_TRANSFER_LENGTH < priv->len) { + /* fprintf (stderr, "Send %d\n", MAX_TRANSFER_LENGTH); */ + ccnet_processor_send_response ( + processor, SC_SERVER_MORE, SS_SERVER_MORE, + priv->buf + priv->off, MAX_TRANSFER_LENGTH); + priv->off += MAX_TRANSFER_LENGTH; + } else { + /* fprintf (stderr, "Send %d\n", priv->len - priv->off); */ + ccnet_processor_send_response ( + processor, SC_SERVER_RET, SS_SERVER_RET, + priv->buf + priv->off, priv->len - priv->off); + g_free (priv->buf); + /* ccnet_processor_done (processor, TRUE); */ + } + return; + } + + ccnet_processor_send_response (processor, SC_BAD_UPDATE_CODE, + SS_BAD_UPDATE_CODE, NULL, 0); + + ccnet_warning ("[rpc-server] Bad update: %s %s.\n", code, code_msg); + + if (priv->buf) + g_free (priv->buf); + ccnet_processor_done (processor, FALSE); +} diff --git a/net/common/processors/rpcserver-proc.h b/net/common/processors/rpcserver-proc.h new file mode 100644 index 0000000..af20539 --- /dev/null +++ b/net/common/processors/rpcserver-proc.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_RPCSERVER_PROC_H +#define CCNET_RPCSERVER_PROC_H + +#include +#include "processor.h" + +#define CCNET_TYPE_RPCSERVER_PROC (ccnet_rpcserver_proc_get_type ()) +#define CCNET_RPCSERVER_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_RPCSERVER_PROC, CcnetRpcserverProc)) +#define CCNET_IS_RPCSERVER_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_RPCSERVER_PROC)) +#define CCNET_RPCSERVER_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_RPCSERVER_PROC, CcnetRpcserverProcClass)) +#define IS_CCNET_RPCSERVER_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_RPCSERVER_PROC)) +#define CCNET_RPCSERVER_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_RPCSERVER_PROC, CcnetRpcserverProcClass)) + +typedef struct _CcnetRpcserverProc CcnetRpcserverProc; +typedef struct _CcnetRpcserverProcClass CcnetRpcserverProcClass; + +struct _CcnetRpcserverProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetRpcserverProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_rpcserver_proc_get_type (); + +#endif + diff --git a/net/common/processors/sample-master-proc.c b/net/common/processors/sample-master-proc.c new file mode 100644 index 0000000..d97e292 --- /dev/null +++ b/net/common/processors/sample-master-proc.c @@ -0,0 +1,217 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "timer.h" +#include "peer.h" +#include "session.h" + +#include "sample-master-proc.h" + +#define DEBUG_FLAG CCNET_DEBUG_PROCESSOR +#include "log.h" + +enum { + INIT, + REQUEST_SEND, + CONNECTED, +}; + + +typedef struct { + GHashTable *registered; + int rate; +} CcnetSampleProcPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_SAMPLE_PROC, CcnetSampleProcPriv)) + +G_DEFINE_TYPE (CcnetSampleProc, ccnet_sample_proc, CCNET_TYPE_PROCESSOR); + + +static int sample_start (CcnetProcessor *processor, int argc, char **argv); +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); +static void disconnect_signals (CcnetProcessor *processor) +{ +} + +static void +set_property (GObject *object, guint property_id, + const GValue *v, GParamSpec *pspec) +{ + CcnetSampleProcPriv *priv = GET_PRIV(object); + + switch (property_id) { + case P_RATE: + priv->rate = g_value_get_int (v); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +get_property (GObject *object, guint property_id, + GValue *v, GParamSpec *pspec) +{ + CcnetSampleProcPriv *priv = GET_PRIV(object); + + switch (property_id) { + case P_RATE: + g_value_set_int (v, priv->rate); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +release_resource(CcnetProcessor *processor) +{ + /* Release the resource hold by this processor here. + * + * It will always be called either when the processor is shutdown + * abnormally (via call ccnet_processor_shutdown()), + * or when it finishes its task normally (via call ccnet_processor_done()). + * + * Note, you must release `all the timers' and `disconnect all the signals'. + * The `retry_timer' will be freed by the default_release_resource() + * in chain up. + */ + + CcnetSampleProcPriv *priv = GET_PRIV (processor); + + /* clean the items in hash table, but do not unref the hash table self. */ + g_hash_table_remove_all (priv->registered); + + /* if your processor connected any signals, disconnect it here */ + disconnect_signals (processor); + + /* should always chain up */ + CCNET_PROCESSOR_CLASS(ccnet_sample_proc_parent_class)->release_resource (processor); +} + +static void shutdown (CcnetProcessor *processor) +{ + /* The processor is shutdown abnormally. */ + + /* the release_resource() will be called after calling shutdown(), + * so only do things that release_resource() does not do. */ + + /* Do not chain up here. */ +} + +static void finalize (GObject *gobject); + +static void +ccnet_sample_proc_class_init (CcnetSampleProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + proc_class->name = "sample-master-proc"; + proc_class->start = sample_start; + proc_class->handle_response = handle_response; + proc_class->shutdown = shutdown; + proc_class->release_resource = release_resource; + + gobject_class->set_property = set_property; + gobject_class->get_property = get_property; + gobject_class->finalize = finalize; + + g_object_class_install_property (gobject_class, P_RATE, + g_param_spec_int ( "rate", NULL, "Transfer Rate", + 0, INT_MAX, 0, G_PARAM_READWRITE)); + + g_type_class_add_private (klass, sizeof (CcnetSendfileProcPriv)); +} + +static void +ccnet_sample_proc_init (CcnetSampleProc *processor) +{ + /* + * The processor used be used multi-times for different peers. + * And this function will only be called once. + * + * So only initialize things that will be used cross sessions. + * + * Normally, use start() for initialization, and use release_resouce() + * for finalization. + */ + + CcnetSampleProcPriv *priv = GET_PRIV (processor); + priv->registered = g_hash_table_new (g_direct_hash, g_direct_equal); +} + +static void finalize (GObject *gobject) +{ + /* undo the things init() do */ + + CcnetSampleProcPriv *priv = GET_PRIV (processor); + + g_hash_table_unref (priv->registered); + + G_OBJECT_CLASS (ccnet_sample_proc_parent_class)->finalize (gobject); +} + + + +static int timeout_cb(CcnetProcessor *processor) +{ + ccnet_warning ("sample slave does not reponse to us\n"); + ccnet_processor_done (processor, FALSE); + + /* return FALSE to cancel the timer */ + return FALSE; +} + +static int +sample_start (CcnetProcessor *processor, int argc, char **argv) +{ + CcnetSampleProcPriv *priv = GET_PRIV (processor); + + /* initialization here */ + priv->rate = DEFAULT_RATE; + processor->state = INIT; + + /* send request */ + ccnet_processor_send_request (processor, "sample-slave"); + + processor->state = REQUEST_SENT; + + /* for convenient, the processor provide a retry_timer, you can + * use it for other purpose. This timer will be freed automatically + * in the default_shutdown() of CcnetProcessor base class. + * + * So if you override the default_shutdown(), you should + * free the timer yourself. + */ + processor->retry_timer = ccnet_timer_new ((TimerCB)timeout_cb, processor, + 10 * 1000); + + + return 0; +} + + +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + if (memcpy(code, "200", 3) == 0) { + ccnet_processor_send_update (processor, "200", "OK", "sample update", + strlen("sample update") + 1); + ccnet_processor_done (processor, TRUE); + } else { + /* code and code_msg are ended with '\0' */ + ccnet_warning ("Bad response from peer %s(%.8s), %s:%s\n", + processor->peer->name, processor->peer->id, + code, code_msg); + ccnet_processor_done (processor, FALSE); + } +} + diff --git a/net/common/processors/sample-master-proc.h b/net/common/processors/sample-master-proc.h new file mode 100644 index 0000000..89c1569 --- /dev/null +++ b/net/common/processors/sample-master-proc.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_SAMPLE_PROC_H +#define CCNET_SAMPLE_PROC_H + +#include + +#include "processor.h" + +#define CCNET_TYPE_SAMPLE_PROC (ccnet_sample_proc_get_type ()) +#define CCNET_SAMPLE_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_SAMPLE_PROC, CcnetSampleProc)) +#define CCNET_IS_SAMPLE_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_SAMPLE_PROC)) +#define CCNET_SAMPLE_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_SAMPLE_PROC, CcnetSampleProcClass)) +#define CCNET_IS_SAMPLE_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_SAMPLE_PROC)) +#define CCNET_SAMPLE_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_SAMPLE_PROC, CcnetSampleProcClass)) + +typedef struct _CcnetSampleProc CcnetSampleProc; +typedef struct _CcnetSampleProcClass CcnetSampleProcClass; + +struct _CcnetSampleProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetSampleProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_sample_proc_get_type (); + +#endif diff --git a/net/common/processors/sample-slave-proc.c b/net/common/processors/sample-slave-proc.c new file mode 100644 index 0000000..08e5d7f --- /dev/null +++ b/net/common/processors/sample-slave-proc.c @@ -0,0 +1,130 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + + +#include "sample-slave-proc.h" + +#include "peer.h" +#include "session.h" + +#define DEBUG_FLAG CCNET_DEBUG_PROCESSOR +#include "log.h" + +enum { + INIT +}; + +typedef struct { + int rate; +} CcnetSampleSlaveProcPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_SAMPLE_SLAVE_PROC, CcnetSampleSlaveProcPriv)) + + +G_DEFINE_TYPE (CcnetSampleSlaveProc, ccnet_sample_slave_proc, CCNET_TYPE_PROCESSOR) + +static int sample_slave_start (CcnetProcessor *processor, int argc, char **argv); +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void +release_resource(CcnetProcessor *processor) +{ + /* Release the resource hold by this processor here. + * + * It will always be called either when the processor is shutdown + * abnormally (via call ccnet_processor_shutdown()), + * or when it finishes its task normally (via call ccnet_processor_done()). + * + * Note, you must release `all the timers' and `disconnect all the signals'. + * The `retry_timer' will be freed by the default_release_resource() + * in chain up. + */ + + + /* should always chain up */ + CCNET_PROCESSOR_CLASS(ccnet_sample_slave_proc_parent_class)->release_resource (gobject); +} + +static void shutdown (CcnetProcessor *processor) +{ + /* The processor is shutdown abnormally. */ + + /* the release_resource() will be called after calling shutdown(), + * so only do things that release_resource() does not do. */ + + /* Do not chain up here. */ +} + +static void +ccnet_sample_slave_proc_class_init (CcnetSampleSlaveProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + proc_class->name = "sample-slave-proc"; + proc_class->start = sample_slave_start; + proc_class->handle_update = handle_update; + proc_class->shutdown = shutdown; + gobject_class->finalize = sample_slave_finalize; + + g_type_class_add_private (klass, sizeof(CcnetSampleSlaveProcPriv)); +} + +static void +ccnet_sample_slave_proc_init (CcnetSampleSlaveProc *processor) +{ + /* + * The processor used be used multi-times for different peers. + * And this function will only be called once. + * + * So only initialize things that will be used cross sessions. + * + * Normally, use start() for initialization, and use release_resouce() + * for finalization. + */ +} + + + + +static void shutdown (CcnetProcessor *processor) +{ + /* The processor is shutdown abnormally. */ + + /* the release_resource() will be called after calling shutdown(), + * so only do things that release_resource() does not do. */ + + /* Do not chain up here. */ +} + + +static int sample_slave_start (CcnetProcessor *processor, int argc, char **argv) +{ + CcnetSampleSlaveProcPriv *priv = GET_PRIV (processor); + priv->rate = DEFAULT_RATE; + processor->state = INIT; + + ccnet_processor_send_response (processor, "200", "OK", "sample response", + strlen("sample response") + 1); + + return 0; +} + +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + if (memcpy(code, "200", 3) == 0) { + ccnet_processor_done (processor, TRUE); + } else { + /* code and code_msg are ended with '\0' */ + ccnet_warning ("Bad update from peer %s(%.8s), %s:%s\n", + processor->peer->name, processor->peer->id, + code, code_msg); + ccnet_processor_done (processor, FALSE); + } +} diff --git a/net/common/processors/sample-slave-proc.h b/net/common/processors/sample-slave-proc.h new file mode 100644 index 0000000..98d3589 --- /dev/null +++ b/net/common/processors/sample-slave-proc.h @@ -0,0 +1,29 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_SAMPLESLAVE_PROC_H +#define CCNET_SAMPLESLAVE_PROC_H + +#include +#include "processor.h" + +#define CCNET_TYPE_SAMPLE_SLAVE_PROC (ccnet_sample_slave_proc_get_type ()) +#define CCNET_SAMPLE_SLAVE_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_SAMPLE_SLAVE_PROC, CcnetSampleSlaveProc)) +#define CCNET_IS_SAMPLE_SLAVE_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_SAMPLE_SLAVE_PROC)) +#define CCNET_SAMPLE_SLAVE_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_SAMPLE_SLAVE_PROC, CcnetSampleSlaveProcClass)) +#define CCNET_IS_SAMPLE_SLAVE_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_SAMPLE_SLAVE_PROC)) +#define CCNET_SAMPLE_SLAVE_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_SAMPLE_SLAVE_PROC, CcnetSampleSlaveProcClass)) + +typedef struct _CcnetSampleSlaveProc CcnetSampleSlaveProc; +typedef struct _CcnetSampleSlaveProcClass CcnetSampleSlaveProcClass; + +struct _CcnetSampleSlaveProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetSampleSlaveProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_sample_slave_proc_get_type (); + +#endif diff --git a/net/common/processors/sendmsg-proc.c b/net/common/processors/sendmsg-proc.c new file mode 100644 index 0000000..59a8b27 --- /dev/null +++ b/net/common/processors/sendmsg-proc.c @@ -0,0 +1,145 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include + +#include "session.h" +#include "peer.h" +#include "message.h" +#include "sendmsg-proc.h" +#include "bloom-filter.h" +#include "message-manager.h" + +#define DEBUG_FLAG CCNET_DEBUG_MESSAGE +#include "log.h" + +enum { + REQUEST_SENT, + CONNECTED +}; + +typedef struct { + CcnetMessage *message; +} CcnetSendmsgProcPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_SENDMSG_PROC, CcnetSendmsgProcPriv)) + +static int send_msg_start (CcnetProcessor *processor, int argc, char **argv); +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +G_DEFINE_TYPE (CcnetSendmsgProc, ccnet_sendmsg_proc, CCNET_TYPE_PROCESSOR) + +static void +release_resource(CcnetProcessor *processor) +{ + CcnetSendmsgProcPriv *priv = GET_PRIV (processor); + + ccnet_message_unref (priv->message); + + CCNET_PROCESSOR_CLASS (ccnet_sendmsg_proc_parent_class)->release_resource (processor); +} + +static void +ccnet_sendmsg_proc_class_init (CcnetSendmsgProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + /* GObjectClass *object_class = G_OBJECT_CLASS (klass); */ + + proc_class->name = "sendmsg-proc"; + proc_class->start = send_msg_start; + proc_class->handle_response = handle_response; + proc_class->release_resource = release_resource; + + g_type_class_add_private (klass, sizeof (CcnetSendmsgProcPriv)); +} + +static void +ccnet_sendmsg_proc_init (CcnetSendmsgProc *processor) +{ +} + +static int +send_msg_start (CcnetProcessor *processor, int argc, char **argv) +{ + char buf[256]; + int len; + + len = snprintf (buf, 256, "receive-msg"); + ccnet_processor_send_request (processor, buf); + processor->state = REQUEST_SENT; + + return 0; +} + +#if 0 +static gboolean +need_send_bloom (CcnetProcessor *processor, CcnetMessage *msg) +{ + CcnetGroupManager *group_mgr = processor->session->groupMgr; + CcnetGroup *group; + + /* just pass through relayed message */ + if (g_strcmp0 (processor->session->base.id, msg->from) != 0) + return TRUE; + + group = ccnet_group_manager_get_group (group_mgr, + msg->to); + if (!group) + return FALSE; + if (!ccnet_group_is_a_maintainer (group, processor->peer->id)) + return FALSE; + + return TRUE; +} +#endif + +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + CcnetSendmsgProcPriv *priv = GET_PRIV (processor); + GString *msg_buf; + + switch (processor->state) { + case REQUEST_SENT: + processor->state = CONNECTED; + + char save_flags = priv->message->flags; + /* if ((priv->message->flags & FLAG_TO_GROUP) && */ + /* (priv->message->flags & FLAG_WITH_BLOOM) && */ + /* !need_send_bloom (processor, priv->message)) */ + /* priv->message->flags &= ~FLAG_WITH_BLOOM; */ + + msg_buf = g_string_new (NULL); + ccnet_message_to_string_buf (priv->message, msg_buf); + ccnet_processor_send_update (processor, + "200", NULL, msg_buf->str, + msg_buf->len+1); /* including '\0' */ + g_string_free (msg_buf, TRUE); + + priv->message->flags = save_flags; + break; + case CONNECTED: + ccnet_processor_done (processor, TRUE); + break; + default: + break; + } +} + + +int +ccnet_sendmsg_proc_set_msg (CcnetSendmsgProc *sendmsg_proc, + CcnetMessage *message) +{ + CcnetSendmsgProcPriv *priv = GET_PRIV (sendmsg_proc); + + priv->message = message; + ccnet_message_ref (message); + + return 0; +} diff --git a/net/common/processors/sendmsg-proc.h b/net/common/processors/sendmsg-proc.h new file mode 100644 index 0000000..4d0fd3a --- /dev/null +++ b/net/common/processors/sendmsg-proc.h @@ -0,0 +1,33 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_SENDMSG_PROC_H +#define CCNET_SENDMSG_PROC_H + +#include + +#include "processor.h" + +#define CCNET_TYPE_SENDMSG_PROC (ccnet_sendmsg_proc_get_type ()) +#define CCNET_SENDMSG_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_SENDMSG_PROC, CcnetSendmsgProc)) +#define CCNET_IS_SENDMSG_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_SENDMSG_PROC)) +#define CCNET_SENDMSG_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_SENDMSG_PROC, CcnetSendmsgProcClass)) +#define CCNET_IS_SENDMSG_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_SENDMSG_PROC)) +#define CCNET_SENDMSG_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_SENDMSG_PROC, CcnetSendmsgProcClass)) + +typedef struct _CcnetSendmsgProc CcnetSendmsgProc; +typedef struct _CcnetSendmsgProcClass CcnetSendmsgProcClass; + +struct _CcnetSendmsgProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetSendmsgProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_sendmsg_proc_get_type (); + +int ccnet_sendmsg_proc_set_msg (CcnetSendmsgProc *sendmsg_proc, + CcnetMessage *msg); + +#endif diff --git a/net/common/processors/sendsessionkey-proc.c b/net/common/processors/sendsessionkey-proc.c new file mode 100644 index 0000000..3ec2757 --- /dev/null +++ b/net/common/processors/sendsessionkey-proc.c @@ -0,0 +1,188 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + + After we get a peer's pubkey, we generate a session key (symmetric) to + encrypt important data. + + receive-session-key + A ---------------------------> B + + SC_SESSION_KEY (ask for session key) + <--------------------------- + + + SC_SESSION_KEY (encrypted with B's pubkey) + ----------------------------> + + SC_OK + <------------------------------- +*/ + +#include +#include + +#include "session.h" +#include "common.h" +#include "processor.h" +#include "peer-mgr.h" +#include "peer.h" +#include "log.h" +#include "rsa.h" + +#include "sendsessionkey-proc.h" + +#define SC_SESSION_KEY "300" +#define SS_SESSION_KEY "session key" +#define SC_ALREADY_HAS_KEY "301" +#define SS_ALREADY_HAS_KEY "already has your session key" +#define SC_BAD_KEY "302" +#define SS_BAD_KEY "bad session key" + +enum { + INIT = 0, + REQUEST_SENT, + SESSION_KEY_SENT, +}; + +typedef struct { + char key[40]; + int state; +} CcnetSendsessionkeyProcPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_SENDSESSIONKEY_PROC, CcnetSendsessionkeyProcPriv)) + +#define USE_PRIV \ + CcnetSendsessionkeyProcPriv *priv = GET_PRIV(processor); + + +G_DEFINE_TYPE (CcnetSendsessionkeyProc, ccnet_sendsessionkey_proc, CCNET_TYPE_PROCESSOR) + +static int start (CcnetProcessor *processor, int argc, char **argv); +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void +release_resource(CcnetProcessor *processor) +{ + CCNET_PROCESSOR_CLASS (ccnet_sendsessionkey_proc_parent_class)->release_resource (processor); +} + + +static void +ccnet_sendsessionkey_proc_class_init (CcnetSendsessionkeyProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->name = "send-session-key"; + proc_class->start = start; + proc_class->handle_response = handle_response; + proc_class->release_resource = release_resource; + + g_type_class_add_private (klass, sizeof (CcnetSendsessionkeyProcPriv)); +} + +static void +ccnet_sendsessionkey_proc_init (CcnetSendsessionkeyProc *processor) +{ +} + + +static int +start (CcnetProcessor *processor, int argc, char **argv) +{ + USE_PRIV; + if (argc != 0) { + ccnet_processor_done (processor, FALSE); + return -1; + } + + if (processor->peer->session_key) { + ccnet_warning ("peer %.10s already has session key\n", + processor->peer->id); + ccnet_processor_done (processor, FALSE); + return -1; + } + + ccnet_processor_send_request (processor, "receive-session-key"); + + priv->state = REQUEST_SENT; + + return 0; +} + +/* random bytes -> sha1 -> pubkey_encrypt -> transmit to peer */ +static unsigned char * +generate_session_key (CcnetProcessor *processor, int *len_p) +{ + USE_PRIV; + CcnetPeer *peer = processor->peer; + unsigned char sha1[20]; + unsigned char *enc_out = NULL; + unsigned char random_buf[40]; + SHA_CTX s; + + RAND_pseudo_bytes (random_buf, sizeof(random_buf)); + + SHA1_Init (&s); + SHA1_Update (&s, random_buf, sizeof(random_buf)); + SHA1_Final (sha1, &s); + + rawdata_to_hex (sha1, priv->key, 20); + + enc_out = public_key_encrypt (peer->pubkey, (unsigned char *)priv->key, + 40, len_p); + + if (*len_p <= 0) { + g_free (enc_out); + return NULL; + } + + return enc_out; +} + +static void +handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + USE_PRIV; + if (strcmp(code, SC_SESSION_KEY) == 0 && priv->state == REQUEST_SENT) { + unsigned char *enc_out = NULL; + int len = 0; + + enc_out = generate_session_key(processor, &len); + if (enc_out) { + ccnet_processor_send_update (processor, + SC_SESSION_KEY, + SS_SESSION_KEY, + (char *)enc_out, len); + g_free (enc_out); + priv->state = SESSION_KEY_SENT; + + } else { + ccnet_warning ("failed to generate session key for peer %.10s\n", + processor->peer->id); + ccnet_processor_done (processor, FALSE); + } + + } else if (strcmp(code, SC_OK) == 0 && priv->state == SESSION_KEY_SENT) { + processor->peer->session_key = g_strndup(priv->key, 40); + + ccnet_peer_manager_on_peer_session_key_sent (processor->peer->manager, + processor->peer); + + ccnet_processor_done (processor, TRUE); + + } else if (strcmp(code, SC_ALREADY_HAS_KEY) == 0) { + /* already has session key, skip */ + ccnet_processor_done (processor, TRUE); + + } else { + ccnet_warning ("[send session key] bad response %s:%s\n", + code, code_msg); + ccnet_processor_done (processor, FALSE); + } +} diff --git a/net/common/processors/sendsessionkey-proc.h b/net/common/processors/sendsessionkey-proc.h new file mode 100644 index 0000000..ce97fa4 --- /dev/null +++ b/net/common/processors/sendsessionkey-proc.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_SENDSESSIONKEY_PROC_H +#define CCNET_SENDSESSIONKEY_PROC_H + +#include + + +#define CCNET_TYPE_SENDSESSIONKEY_PROC (ccnet_sendsessionkey_proc_get_type ()) +#define CCNET_SENDSESSIONKEY_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_SENDSESSIONKEY_PROC, CcnetSendsessionkeyProc)) +#define CCNET_IS_SENDSESSIONKEY_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_SENDSESSIONKEY_PROC)) +#define CCNET_SENDSESSIONKEY_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_SENDSESSIONKEY_PROC, CcnetSendsessionkeyProcClass)) +#define IS_CCNET_SENDSESSIONKEY_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_SENDSESSIONKEY_PROC)) +#define CCNET_SENDSESSIONKEY_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_SENDSESSIONKEY_PROC, CcnetSendsessionkeyProcClass)) + +typedef struct _CcnetSendsessionkeyProc CcnetSendsessionkeyProc; +typedef struct _CcnetSendsessionkeyProcClass CcnetSendsessionkeyProcClass; + +struct _CcnetSendsessionkeyProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetSendsessionkeyProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_sendsessionkey_proc_get_type (); + +#endif + diff --git a/net/common/processors/sendsessionkey-v2-proc.c b/net/common/processors/sendsessionkey-v2-proc.c new file mode 100644 index 0000000..c1c6924 --- /dev/null +++ b/net/common/processors/sendsessionkey-v2-proc.c @@ -0,0 +1,197 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + + After we get a peer's pubkey, we generate a session key (symmetric) to + encrypt important data. + + receive-skey-v2 [--enc-channel] + A -------------------------------------------> B + + SC_SESSION_KEY + <------------------------------- + + SC_SESSION_KEY (encrypted with B's pubkey) + ----------------------------> + + SC_OK_ENCRYPT Or SC_NO_ENCRYPT + <------------------------------------------ +*/ + +#include +#include + +#include "session.h" +#include "common.h" +#include "processor.h" +#include "peer-mgr.h" +#include "peer.h" +#include "log.h" +#include "rsa.h" + +#include "sendsessionkey-v2-proc.h" + +#define SC_SESSION_KEY "300" +#define SS_SESSION_KEY "session key" +#define SC_ALREADY_HAS_KEY "301" +#define SS_ALREADY_HAS_KEY "already has your session key" +#define SC_NO_ENCRYPT "303" +#define SS_NO_ENCRYPT "Donot encrypt channel" +#define SC_BAD_KEY "400" +#define SS_BAD_KEY "bad session key" + + +enum { + INIT = 0, + REQUEST_SENT, + SESSION_KEY_SENT, +}; + +typedef struct { + char key[40]; + int state; +} CcnetSendskey2ProcPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_SENDSKEY2_PROC, CcnetSendskey2ProcPriv)) + +#define USE_PRIV \ + CcnetSendskey2ProcPriv *priv = GET_PRIV(processor); + + +G_DEFINE_TYPE (CcnetSendskey2Proc, ccnet_sendskey2_proc, CCNET_TYPE_PROCESSOR) + +static int start (CcnetProcessor *processor, int argc, char **argv); +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void +release_resource(CcnetProcessor *processor) +{ + CCNET_PROCESSOR_CLASS (ccnet_sendskey2_proc_parent_class)->release_resource (processor); +} + + +static void +ccnet_sendskey2_proc_class_init (CcnetSendskey2ProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->name = "send-skey2"; + proc_class->start = start; + proc_class->handle_response = handle_response; + proc_class->release_resource = release_resource; + + g_type_class_add_private (klass, sizeof (CcnetSendskey2ProcPriv)); +} + +static void +ccnet_sendskey2_proc_init (CcnetSendskey2Proc *processor) +{ +} + + +static int +start (CcnetProcessor *processor, int argc, char **argv) +{ + USE_PRIV; + if (argc != 0) { + ccnet_processor_done (processor, FALSE); + return -1; + } + + gboolean encrypt_channel = ccnet_session_should_encrypt_channel ( + processor->session); + + if (encrypt_channel) + ccnet_processor_send_request (processor, "receive-skey2 --enc-channel"); + else + ccnet_processor_send_request (processor, "receive-skey2"); + + priv->state = REQUEST_SENT; + + return 0; +} + +/* random bytes -> sha1 -> pubkey_encrypt -> transmit to peer */ +static unsigned char * +generate_session_key (CcnetProcessor *processor, int *len_p) +{ + USE_PRIV; + CcnetPeer *peer = processor->peer; + unsigned char sha1[20]; + unsigned char *enc_out = NULL; + unsigned char random_buf[40]; + SHA_CTX s; + + RAND_pseudo_bytes (random_buf, sizeof(random_buf)); + + SHA1_Init (&s); + SHA1_Update (&s, random_buf, sizeof(random_buf)); + SHA1_Final (sha1, &s); + + rawdata_to_hex (sha1, priv->key, 20); + + enc_out = public_key_encrypt (peer->pubkey, (unsigned char *)priv->key, + 40, len_p); + + if (*len_p <= 0) { + g_free (enc_out); + return NULL; + } + + return enc_out; +} + +static void +handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + USE_PRIV; + if (strcmp(code, SC_SESSION_KEY) == 0 && priv->state == REQUEST_SENT) { + unsigned char *enc_out = NULL; + int len = 0; + + enc_out = generate_session_key(processor, &len); + if (enc_out) { + ccnet_processor_send_update (processor, + SC_SESSION_KEY, + SS_SESSION_KEY, + (char *)enc_out, len); + g_free (enc_out); + priv->state = SESSION_KEY_SENT; + + } else { + ccnet_warning ("failed to generate session key for peer %.10s\n", + processor->peer->id); + ccnet_processor_done (processor, FALSE); + } + + } else if (strcmp(code, SC_OK) == 0 && priv->state == SESSION_KEY_SENT) { + processor->peer->session_key = g_strndup(priv->key, 40); + + if (ccnet_session_should_encrypt_channel (processor->session)) + ccnet_peer_prepare_channel_encryption (processor->peer); + + ccnet_peer_manager_on_peer_session_key_sent (processor->peer->manager, + processor->peer); + + ccnet_processor_done (processor, TRUE); + + } else if (strcmp(code, SC_ALREADY_HAS_KEY) == 0) { + /* already has session key, skip */ + ccnet_processor_done (processor, TRUE); + + } else if (strcmp(code, SC_NO_ENCRYPT) == 0) { + processor->peer->session_key = g_strndup(priv->key, 40); + ccnet_peer_manager_on_peer_session_key_sent (processor->peer->manager, + processor->peer); + ccnet_processor_done (processor, TRUE); + } else { + ccnet_warning ("[send session key] bad response %s:%s\n", + code, code_msg); + ccnet_processor_done (processor, FALSE); + } +} diff --git a/net/common/processors/sendsessionkey-v2-proc.h b/net/common/processors/sendsessionkey-v2-proc.h new file mode 100644 index 0000000..a76110a --- /dev/null +++ b/net/common/processors/sendsessionkey-v2-proc.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_SENDSKEY2_PROC_H +#define CCNET_SENDSKEY2_PROC_H + +#include + + +#define CCNET_TYPE_SENDSKEY2_PROC (ccnet_sendskey2_proc_get_type ()) +#define CCNET_SENDSKEY2_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_SENDSKEY2_PROC, CcnetSendskey2Proc)) +#define CCNET_IS_SENDSKEY2_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_SENDSKEY2_PROC)) +#define CCNET_SENDSKEY2_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_SENDSKEY2_PROC, CcnetSendskey2ProcClass)) +#define IS_CCNET_SENDSKEY2_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_SENDSKEY2_PROC)) +#define CCNET_SENDSKEY2_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_SENDSKEY2_PROC, CcnetSendskey2ProcClass)) + +typedef struct _CcnetSendskey2Proc CcnetSendskey2Proc; +typedef struct _CcnetSendskey2ProcClass CcnetSendskey2ProcClass; + +struct _CcnetSendskey2Proc { + CcnetProcessor parent_instance; +}; + +struct _CcnetSendskey2ProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_sendskey2_proc_get_type (); + +#endif + diff --git a/net/common/processors/service-proxy-proc.c b/net/common/processors/service-proxy-proc.c new file mode 100644 index 0000000..27b93fb --- /dev/null +++ b/net/common/processors/service-proxy-proc.c @@ -0,0 +1,186 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "session.h" +#include "peer.h" +#include "peer-mgr.h" +#include "proc-factory.h" +#include "service-proxy-proc.h" +#include "service-stub-proc.h" + +#define DEBUG_FLAG CCNET_DEBUG_PROCESSOR +#include "log.h" + +#define SC_BAD_CMD_FMT "400" +#define SS_BAD_CMD_FMT "Bad command format" + + + +typedef struct { + CcnetServiceStubProc *stub_proc; + char *name; +} ServiceProxyPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_SERVICE_PROXY_PROC, ServiceProxyPriv)) + +static int service_proxy_start (CcnetProcessor *processor, int argc, char **argv); + +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +/* called by stub processor */ +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +G_DEFINE_TYPE (CcnetServiceProxyProc, ccnet_service_proxy_proc, CCNET_TYPE_PROCESSOR) + +static void +release_resource(CcnetProcessor *processor) +{ + ServiceProxyPriv *priv = GET_PRIV(processor); + if (priv->name) { + g_free (priv->name); + priv->name = NULL; + } + + /* should always chain up */ + CCNET_PROCESSOR_CLASS(ccnet_service_proxy_proc_parent_class)->release_resource (processor); +} + +static void +ccnet_service_proxy_proc_class_init (CcnetServiceProxyProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + /* GObjectClass *object_class = G_OBJECT_CLASS (klass); */ + + proc_class->name = "service-proxy-proc"; + proc_class->start = service_proxy_start; + proc_class->handle_update = handle_update; + proc_class->handle_response = handle_response; + proc_class->release_resource = release_resource; + + g_type_class_add_private (klass, sizeof (ServiceProxyPriv)); +} + +static void +ccnet_service_proxy_proc_init (CcnetServiceProxyProc *processor) +{ + ServiceProxyPriv *priv = GET_PRIV(processor); + priv->name = NULL; +} + +static int +service_proxy_start (CcnetProcessor *processor, int argc, char **argv) +{ + return 0; +} + + +static char* proc_name_strjoin_n (const char *seperator, int argc, char **argv) +{ + GString *buf; + int i; + char *str; + + buf = g_string_new (argv[0]); + for (i = 1; i < argc; ++i) { + g_string_append (buf, seperator); + g_string_append (buf, argv[i]); + } + str = buf->str; + g_string_free (buf, FALSE); + return str; +} + +void +ccnet_service_proxy_invoke_remote (CcnetProcessor *processor, + CcnetPeer *remote, + int argc, char **argv) +{ + CcnetServiceStubProc *stub_proc; + ServiceProxyPriv *priv = GET_PRIV(processor); + + if (argc < 1) { + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + SS_BAD_CMD_FMT, NULL, 0); + ccnet_processor_done (processor, FALSE); + return; + } + + priv->name = proc_name_strjoin_n(" ", argc, argv); + + stub_proc = CCNET_SERVICE_STUB_PROC ( + ccnet_proc_factory_create_master_processor ( + processor->session->proc_factory, "service-stub", remote) + ); + priv->stub_proc = stub_proc; + ccnet_service_stub_proc_set_proxy_proc (stub_proc, processor); + + /* Start can fail if the remote end is not connected. */ + if (ccnet_processor_start (CCNET_PROCESSOR(stub_proc), argc, argv) < 0) { + ccnet_processor_send_response (processor, SC_PROC_DEAD, SS_PROC_DEAD, + NULL, 0); + ccnet_processor_done (processor, FALSE); + } +} + +/* TODO: the same as above, can use one function instead */ +void +ccnet_service_proxy_invoke_local (CcnetProcessor *processor, + CcnetPeer *local, + int argc, char **argv) +{ + CcnetServiceStubProc *stub_proc; + ServiceProxyPriv *priv = GET_PRIV(processor); + + if (argc < 1) { + ccnet_processor_send_response (processor, SC_BAD_CMD_FMT, + SS_BAD_CMD_FMT, NULL, 0); + ccnet_processor_done (processor, FALSE); + return; + } + + priv->name = proc_name_strjoin_n(" ", argc, argv); + + stub_proc = CCNET_SERVICE_STUB_PROC ( + ccnet_proc_factory_create_master_processor ( + processor->session->proc_factory, "service-stub", local) + ); + priv->stub_proc = stub_proc; + ccnet_service_stub_proc_set_proxy_proc (stub_proc, processor); + + ccnet_processor_start (CCNET_PROCESSOR(stub_proc), argc, argv); +} + +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + ServiceProxyPriv *priv = GET_PRIV (processor); + if(!priv->stub_proc || !((CcnetProcessor *)priv->stub_proc)->peer->is_local) + ccnet_debug ("[Svc Proxy] %s:%d [%s] handle update: %s %s\n", + GET_PNAME(processor), PRINT_ID(processor->id), + priv->name, code, code_msg); + + ccnet_processor_handle_update ((CcnetProcessor *)priv->stub_proc, + code, code_msg, content, clen); +} + + +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + ServiceProxyPriv *priv = GET_PRIV (processor); + if(!priv->stub_proc || !((CcnetProcessor *)priv->stub_proc)->peer->is_local) + ccnet_debug ("[Svc Proxy] %s:%d [%s] handle response: %s %s\n", + GET_PNAME(processor), PRINT_ID(processor->id), + priv->name, code, code_msg); + + /* relay this response */ + ccnet_processor_send_response (processor, code, code_msg, content, clen); +} diff --git a/net/common/processors/service-proxy-proc.h b/net/common/processors/service-proxy-proc.h new file mode 100644 index 0000000..48992e8 --- /dev/null +++ b/net/common/processors/service-proxy-proc.h @@ -0,0 +1,38 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_SERVICE_PROXY_PROC_H +#define CCNET_SERVICE_PROXY_PROC_H + +#include + +#include "processor.h" + +#define CCNET_TYPE_SERVICE_PROXY_PROC (ccnet_service_proxy_proc_get_type ()) +#define CCNET_SERVICE_PROXY_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_SERVICE_PROXY_PROC, CcnetServiceProxyProc)) +#define CCNET_IS_SERVICE_PROXY_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_SERVICE_PROXY_PROC)) +#define CCNET_SERVICE_PROXY_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_SERVICE_PROXY_PROC, CcnetServiceProxyProcClass)) +#define CCNET_IS_SERVICE_PROXY_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_SERVICE_PROXY_PROC)) +#define CCNET_SERVICE_PROXY_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_SERVICE_PROXY_PROC, CcnetServiceProxyProcClass)) + +typedef struct _CcnetServiceProxyProc CcnetServiceProxyProc; +typedef struct _CcnetServiceProxyProcClass CcnetServiceProxyProcClass; + +struct _CcnetServiceProxyProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetServiceProxyProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_service_proxy_proc_get_type (); + +void ccnet_service_proxy_invoke_remote (CcnetProcessor *processor, + CcnetPeer *remote, + int argc, char **argv); + +void ccnet_service_proxy_invoke_local (CcnetProcessor *processor, + CcnetPeer *local, + int argc, char **argv); + +#endif diff --git a/net/common/processors/service-stub-proc.c b/net/common/processors/service-stub-proc.c new file mode 100644 index 0000000..3f5bc81 --- /dev/null +++ b/net/common/processors/service-stub-proc.c @@ -0,0 +1,122 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "peer.h" +#include "service-stub-proc.h" +#include "service-proxy-proc.h" + +#define DEBUG_FLAG CCNET_DEBUG_OTHER +#include "log.h" + +typedef struct { + CcnetServiceProxyProc *proxy_proc; +} ServiceStubPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_SERVICE_STUB_PROC, ServiceStubPriv)) + +static int service_stub_start (CcnetProcessor *processor, int argc, char **argv); + +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +G_DEFINE_TYPE (CcnetServiceStubProc, ccnet_service_stub_proc, CCNET_TYPE_PROCESSOR) + + +static void +ccnet_service_stub_proc_class_init (CcnetServiceStubProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + /* GObjectClass *object_class = G_OBJECT_CLASS (klass); */ + + proc_class->name = "service-stub-proc"; + proc_class->start = service_stub_start; + proc_class->handle_update = handle_update; + proc_class->handle_response = handle_response; + + g_type_class_add_private (klass, sizeof (ServiceStubPriv)); +} + +static void +ccnet_service_stub_proc_init (CcnetServiceStubProc *processor) +{ +} + +static void +strnjoin (int n, char **strs, GString *buf) +{ + int i; + + if (n == 0) + return; + + g_string_append (buf, strs[0]); + + for (i = 1; i < n; i++) { + g_string_append (buf, " "); + g_string_append (buf, strs[i]); + } +} + +static int +service_stub_start (CcnetProcessor *processor, int argc, char **argv) +{ + GString *buf; + ServiceStubPriv *priv = GET_PRIV (processor); + + g_return_val_if_fail (priv->proxy_proc != NULL, -1); + buf = g_string_new (NULL); + + CcnetProcessor *pproc = (CcnetProcessor *)priv->proxy_proc; + if (!pproc->peer->is_local) { + /* remote? (this may be wrong) */ + g_string_append (buf, "remote "); + g_string_append (buf, pproc->peer->id); + g_string_append (buf, " "); + } + + strnjoin (argc, argv, buf); + ccnet_processor_send_request (processor, buf->str); + g_string_free (buf, TRUE); + + return 0; +} + +void +ccnet_service_stub_proc_set_proxy_proc (CcnetServiceStubProc *proc, + CcnetProcessor *proxy_proc) +{ + ServiceStubPriv *priv = GET_PRIV (proc); + + priv->proxy_proc = (CcnetServiceProxyProc *)proxy_proc; +} + +static void handle_response (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + ServiceStubPriv *priv = GET_PRIV (processor); + + /* ccnet_debug ("[Svc Stub] %d handle response: %s %s\n", */ + /* PRINT_ID(processor->id), code, code_msg); */ + ccnet_processor_handle_response ((CcnetProcessor *)priv->proxy_proc, + code, code_msg, content, clen); +} + + +static +void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + + /* ccnet_debug ("[Svc Stub] %d handle update: %s %s\n", */ + /* PRINT_ID(processor->id), code, code_msg); */ + ccnet_processor_send_update (processor, code, code_msg, content, clen); +} diff --git a/net/common/processors/service-stub-proc.h b/net/common/processors/service-stub-proc.h new file mode 100644 index 0000000..1c269a8 --- /dev/null +++ b/net/common/processors/service-stub-proc.h @@ -0,0 +1,40 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_SERVICE_STUB_PROC_H +#define CCNET_SERVICE_STUB_PROC_H + +#include + +#include "processor.h" + + +#define CCNET_TYPE_SERVICE_STUB_PROC (ccnet_service_stub_proc_get_type ()) +#define CCNET_SERVICE_STUB_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_SERVICE_STUB_PROC, CcnetServiceStubProc)) +#define CCNET_IS_SERVICE_STUB_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_SERVICE_STUB_PROC)) +#define CCNET_SERVICE_STUB_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_SERVICE_STUB_PROC, CcnetServiceStubProcClass)) +#define CCNET_IS_SERVICE_STUB_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_SERVICE_STUB_PROC)) +#define CCNET_SERVICE_STUB_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_SERVICE_STUB_PROC, CcnetServiceStubProcClass)) + +typedef struct _CcnetServiceStubProc CcnetServiceStubProc; +typedef struct _CcnetServiceStubProcClass CcnetServiceStubProcClass; + +struct _CcnetServiceStubProc { + CcnetProcessor parent_instance; + + +}; + +struct _CcnetServiceStubProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_service_stub_proc_get_type (); + +void ccnet_service_stub_proc_set_proxy_proc (CcnetServiceStubProc *proc, + CcnetProcessor *processor); + +void ccnet_service_stub_proc_send_update (CcnetServiceStubProc *proc, + char *code, char *code_msg, + char *content, int clen); + +#endif diff --git a/net/common/processors/threaded-rpcserver-proc.c b/net/common/processors/threaded-rpcserver-proc.c new file mode 100644 index 0000000..e46cdb8 --- /dev/null +++ b/net/common/processors/threaded-rpcserver-proc.c @@ -0,0 +1,152 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "include.h" + +#include "server-session.h" +#include "threaded-rpcserver-proc.h" +#include "searpc-server.h" +#include "rpc-common.h" +#include "job-mgr.h" + +typedef struct { + char *call_buf; + gsize call_len; + char *buf; + gsize len; + int off; + char *error_message; +} CcnetThreadedRpcserverProcPriv; + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_THREADED_RPCSERVER_PROC, CcnetThreadedRpcserverProcPriv)) + +G_DEFINE_TYPE (CcnetThreadedRpcserverProc, ccnet_threaded_rpcserver_proc, CCNET_TYPE_PROCESSOR) + +static int start (CcnetProcessor *processor, int argc, char **argv); +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void +release_resource(CcnetProcessor *processor) +{ + CcnetThreadedRpcserverProcPriv *priv = GET_PRIV (processor); + + g_free (priv->buf); + + CCNET_PROCESSOR_CLASS (ccnet_threaded_rpcserver_proc_parent_class)->release_resource (processor); +} + + +static void +ccnet_threaded_rpcserver_proc_class_init (CcnetThreadedRpcserverProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->start = start; + proc_class->handle_update = handle_update; + proc_class->release_resource = release_resource; + proc_class->name = "threaded-rpcserver-proc"; + + g_type_class_add_private (klass, sizeof(CcnetThreadedRpcserverProcPriv)); +} + +static void +ccnet_threaded_rpcserver_proc_init (CcnetThreadedRpcserverProc *processor) +{ +} + + +static int +start (CcnetProcessor *processor, int argc, char **argv) +{ + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + + return 0; +} + +static void * +call_function_job (void *vprocessor) +{ + CcnetProcessor *processor = vprocessor; + CcnetThreadedRpcserverProcPriv *priv = GET_PRIV(processor); + char *svc_name = processor->name; + + priv->buf = searpc_server_call_function (svc_name, priv->call_buf, priv->call_len, + &priv->len); + g_free (priv->call_buf); + + return vprocessor; +} + +static void +call_function_done (void *vprocessor) +{ + CcnetProcessor *processor = vprocessor; + CcnetThreadedRpcserverProcPriv *priv = GET_PRIV(processor); + + if (priv->buf) { + if (priv->len < MAX_TRANSFER_LENGTH) { + ccnet_processor_send_response (processor, SC_SERVER_RET, SS_SERVER_RET, + priv->buf, priv->len); + g_free (priv->buf); + priv->buf = NULL; + /* ccnet_processor_done (processor, TRUE); */ + return; + } + + /* we need to split data into multiple segments */ + ccnet_processor_send_response (processor, SC_SERVER_MORE, + SS_SERVER_MORE, priv->buf, + MAX_TRANSFER_LENGTH); + priv->off = MAX_TRANSFER_LENGTH; + } else { + char *message = priv->error_message ? priv->error_message : ""; + ccnet_processor_send_response (processor, SC_SERVER_ERR, + message, + NULL, 0); + g_free (priv->error_message); + ccnet_processor_done (processor, FALSE); + } +} + +static void +handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ + CcnetThreadedRpcserverProcPriv *priv = GET_PRIV (processor); + + if (memcmp (code, SC_CLIENT_CALL, 3) == 0) { + priv->call_buf = g_memdup (content, clen); + priv->call_len = (gsize)clen; + ccnet_processor_thread_create (processor, + NULL, + call_function_job, + call_function_done, + processor); + return; + } + + if (memcmp (code, SC_CLIENT_MORE, 3) == 0) { + if (priv->off + MAX_TRANSFER_LENGTH < priv->len) { + ccnet_processor_send_response ( + processor, SC_SERVER_MORE, SS_SERVER_MORE, + priv->buf + priv->off, MAX_TRANSFER_LENGTH); + priv->off += MAX_TRANSFER_LENGTH; + } else { + ccnet_processor_send_response ( + processor, SC_SERVER_RET, SS_SERVER_RET, + priv->buf + priv->off, priv->len - priv->off); + g_free (priv->buf); + priv->buf = NULL; + /* ccnet_processor_done (processor, TRUE); */ + } + return; + } + + ccnet_processor_send_response (processor, SC_BAD_UPDATE_CODE, + SS_BAD_UPDATE_CODE, NULL, 0); + g_warning ("[rpc-server] Bad update: %s %s.\n", code, code_msg); + ccnet_processor_done (processor, FALSE); +} diff --git a/net/common/processors/threaded-rpcserver-proc.h b/net/common/processors/threaded-rpcserver-proc.h new file mode 100644 index 0000000..860f8f3 --- /dev/null +++ b/net/common/processors/threaded-rpcserver-proc.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_THREADED_RPCSERVER_PROC_H +#define CCNET_THREADED_RPCSERVER_PROC_H + +#include +#include "processor.h" + +#define CCNET_TYPE_THREADED_RPCSERVER_PROC (ccnet_threaded_rpcserver_proc_get_type ()) +#define CCNET_THREADED_RPCSERVER_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_THREADED_RPCSERVER_PROC, CcnetThreadedRpcserverProc)) +#define CCNET_IS_THREADED_RPCSERVER_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_THREADED_RPCSERVER_PROC)) +#define CCNET_THREADED_RPCSERVER_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_THREADED_RPCSERVER_PROC, CcnetThreadedRpcserverProcClass)) +#define IS_CCNET_THREADED_RPCSERVER_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_THREADED_RPCSERVER_PROC)) +#define CCNET_THREADED_RPCSERVER_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_THREADED_RPCSERVER_PROC, CcnetThreadedRpcserverProcClass)) + +typedef struct _CcnetThreadedRpcserverProc CcnetThreadedRpcserverProc; +typedef struct _CcnetThreadedRpcserverProcClass CcnetThreadedRpcserverProcClass; + +struct _CcnetThreadedRpcserverProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetThreadedRpcserverProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_threaded_rpcserver_proc_get_type (); + +#endif + diff --git a/net/common/rpc-service.c b/net/common/rpc-service.c new file mode 100644 index 0000000..af399a7 --- /dev/null +++ b/net/common/rpc-service.c @@ -0,0 +1,1549 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + + +#include +#include + +#include "peer.h" +#include "session.h" +#include "peer-mgr.h" + +#include "proc-factory.h" +#include "rpc-service.h" + +#include "ccnet-object.h" + +#include "processors/rpcserver-proc.h" +#ifdef CCNET_SERVER +#include "processors/threaded-rpcserver-proc.h" +#endif +#include "searpc-server.h" +#include "ccnet-config.h" + +#ifdef CCNET_SERVER +#include "server-session.h" +#endif + +#define DEBUG_FLAG CCNET_DEBUG_OTHER +#include "log.h" + +#include "rsa.h" + +#define CCNET_ERR_INTERNAL 500 + +extern CcnetSession *session; + +#include + +#include "searpc-signature.h" +#include "searpc-marshal.h" + +void +ccnet_start_rpc(CcnetSession *session) +{ + searpc_server_init (register_marshals); + + searpc_create_service ("ccnet-rpcserver"); + ccnet_proc_factory_register_processor (session->proc_factory, + "ccnet-rpcserver", + CCNET_TYPE_RPCSERVER_PROC); + +#ifdef CCNET_SERVER + searpc_create_service ("ccnet-threaded-rpcserver"); + ccnet_proc_factory_register_processor (session->proc_factory, + "ccnet-threaded-rpcserver", + CCNET_TYPE_THREADED_RPCSERVER_PROC); +#endif + + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_list_peers, + "list_peers", + searpc_signature_string__void()); + + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_list_resolving_peers, + "list_resolving_peers", + searpc_signature_objlist__void()); + + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_get_peers_by_role, + "get_peers_by_role", + searpc_signature_objlist__string()); + + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_get_peer, + "get_peer", + searpc_signature_object__string()); + + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_get_peer_by_idname, + "get_peer_by_idname", + searpc_signature_object__string()); + + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_update_peer_address, + "update_peer_address", + searpc_signature_int__string_string_int()); + + + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_get_session_info, + "get_session_info", + searpc_signature_object__void()); + + + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_add_client, + "add_client", + searpc_signature_int__string()); + + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_add_role, + "add_role", + searpc_signature_int__string_string()); + + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_remove_role, + "remove_role", + searpc_signature_int__string_string()); + + + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_get_config, + "get_config", + searpc_signature_string__string()); + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_set_config, + "set_config", + searpc_signature_int__string_string()); + + /* RSA encrypt a message with peer's public key. */ + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_pubkey_encrypt, + "pubkey_encrypt", + searpc_signature_string__string_string()); + + /* RSA decrypt a message with my private key. */ + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_privkey_decrypt, + "privkey_decrypt", + searpc_signature_string__string()); + +#ifdef CCNET_SERVER + + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_list_peer_stat, + "list_peer_stat", + searpc_signature_objlist__void()); + + + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_add_emailuser, + "add_emailuser", + searpc_signature_int__string_string_int_int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_remove_emailuser, + "remove_emailuser", + searpc_signature_int__string_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_validate_emailuser, + "validate_emailuser", + searpc_signature_int__string_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_emailuser, + "get_emailuser", + searpc_signature_object__string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_emailuser_with_import, + "get_emailuser_with_import", + searpc_signature_object__string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_emailuser_by_id, + "get_emailuser_by_id", + searpc_signature_object__int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_emailusers, + "get_emailusers", + searpc_signature_objlist__string_int_int_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_search_emailusers, + "search_emailusers", + searpc_signature_objlist__string_string_int_int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_search_ldapusers, + "search_ldapusers", + searpc_signature_objlist__string_int_int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_count_emailusers, + "count_emailusers", + searpc_signature_int64__string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_count_inactive_emailusers, + "count_inactive_emailusers", + searpc_signature_int64__string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_update_emailuser, + "update_emailuser", + searpc_signature_int__string_int_string_int_int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_update_role_emailuser, + "update_role_emailuser", + searpc_signature_int__string_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_superusers, + "get_superusers", + searpc_signature_objlist__void()); + + /* RSA sign a message with my private key. */ + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_sign_message, + "sign_message", + searpc_signature_string__string()); + + /* Verify a message with a peer's public key */ + searpc_server_register_function ("ccnet-rpcserver", + ccnet_rpc_verify_message, + "verify_message", + searpc_signature_int__string_string_string()); + + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_create_group, + "create_group", + searpc_signature_int__string_string_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_create_org_group, + "create_org_group", + searpc_signature_int__int_string_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_remove_group, + "remove_group", + searpc_signature_int__int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_group_add_member, + "group_add_member", + searpc_signature_int__int_string_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_group_remove_member, + "group_remove_member", + searpc_signature_int__int_string_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_group_set_admin, + "group_set_admin", + searpc_signature_int__int_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_group_unset_admin, + "group_unset_admin", + searpc_signature_int__int_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_set_group_name, + "set_group_name", + searpc_signature_int__int_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_quit_group, + "quit_group", + searpc_signature_int__int_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_groups, + "get_groups", + searpc_signature_objlist__string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_all_groups, + "get_all_groups", + searpc_signature_objlist__int_int_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_group, + "get_group", + searpc_signature_object__int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_group_members, + "get_group_members", + searpc_signature_objlist__int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_check_group_staff, + "check_group_staff", + searpc_signature_int__int_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_remove_group_user, + "remove_group_user", + searpc_signature_int__string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_is_group_user, + "is_group_user", + searpc_signature_int__int_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_set_group_creator, + "set_group_creator", + searpc_signature_int__int_string()); + + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_create_org, + "create_org", + searpc_signature_int__string_string_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_remove_org, + "remove_org", + searpc_signature_int__int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_all_orgs, + "get_all_orgs", + searpc_signature_objlist__int_int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_count_orgs, + "count_orgs", + searpc_signature_int64__void()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_org_by_url_prefix, + "get_org_by_url_prefix", + searpc_signature_object__string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_org_by_id, + "get_org_by_id", + searpc_signature_object__int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_add_org_user, + "add_org_user", + searpc_signature_int__int_string_int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_remove_org_user, + "remove_org_user", + searpc_signature_int__int_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_orgs_by_user, + "get_orgs_by_user", + searpc_signature_objlist__string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_org_emailusers, + "get_org_emailusers", + searpc_signature_objlist__string_int_int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_add_org_group, + "add_org_group", + searpc_signature_int__int_int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_remove_org_group, + "remove_org_group", + searpc_signature_int__int_int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_is_org_group, + "is_org_group", + searpc_signature_int__int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_org_id_by_group, + "get_org_id_by_group", + searpc_signature_int__int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_get_org_groups, + "get_org_groups", + searpc_signature_objlist__int_int_int()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_org_user_exists, + "org_user_exists", + searpc_signature_int__int_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_is_org_staff, + "is_org_staff", + searpc_signature_int__int_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_set_org_staff, + "set_org_staff", + searpc_signature_int__int_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_unset_org_staff, + "unset_org_staff", + searpc_signature_int__int_string()); + searpc_server_register_function ("ccnet-threaded-rpcserver", + ccnet_rpc_set_org_name, + "set_org_name", + searpc_signature_int__int_string()); + + +#endif /* CCNET_SERVER */ + +} + +char * +ccnet_rpc_list_peers(GError **error) +{ + CcnetPeerManager *peer_mgr = session->peer_mgr; + GList *peer_list, *ptr; + GString *result; + CcnetPeer *peer; + + peer_list = ccnet_peer_manager_get_peer_list(peer_mgr); + if (peer_list == NULL) { + g_set_error(error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Failed to get peer list"); + return NULL; + } + + result = g_string_new(""); + ptr = peer_list; + while (ptr) { + peer = ptr->data; + g_string_append_printf(result, "%s\n", peer->id); + ptr = ptr->next; + } + g_list_free(peer_list); + + return g_string_free(result, FALSE); +} + +GList * +ccnet_rpc_list_resolving_peers (GError **error) +{ + CcnetPeerManager *peer_mgr = session->peer_mgr; + return ccnet_peer_manager_get_resolve_peers(peer_mgr); +} + +GList * +ccnet_rpc_get_peers_by_role(const char *role, GError **error) +{ + CcnetPeerManager *peer_mgr = session->peer_mgr; + return ccnet_peer_manager_get_peers_with_role (peer_mgr, role); +} + + +GObject * +ccnet_rpc_get_peer(const char *peer_id, GError **error) +{ + if (!peer_id) + return NULL; + + CcnetPeerManager *peer_mgr = session->peer_mgr; + CcnetPeer *peer = ccnet_peer_manager_get_peer(peer_mgr, peer_id); + return (GObject*)peer; +} + +int +ccnet_rpc_update_peer_address (const char *peer_id, + const char *addr, + int port, + GError **error) +{ + if (!peer_id || !addr || port <= 0 || port > 65536) { + g_set_error(error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Invalid arguments"); + return -1; + } + + CcnetPeer *peer = ccnet_peer_manager_get_peer (session->peer_mgr, peer_id); + if (!peer) { + return -1; + } + + ccnet_peer_manager_set_peer_public_addr (session->peer_mgr, peer, addr, port); + g_object_unref (peer); + + return 0; +} + +GObject * +ccnet_rpc_get_peer_by_idname(const char *idname, GError **error) +{ + if (!idname) + return NULL; + + CcnetPeerManager *peer_mgr = session->peer_mgr; + CcnetPeer *peer = ccnet_peer_manager_get_peer(peer_mgr, idname); + if (!peer) + peer = ccnet_peer_manager_get_peer_by_name (peer_mgr, idname); + if (peer) { + return (GObject*)peer; + } + return NULL; +} + +GObject * +ccnet_rpc_get_session_info(GError **error) +{ + g_object_ref (session); + return (GObject*)session; +} + +int +ccnet_rpc_add_client(const char *peer_id, GError **error) +{ + CcnetPeerManager *mgr = session->peer_mgr; + CcnetPeer *peer; + + if (strlen(peer_id) != 40) { + g_set_error(error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Peer ID must be of length 40"); + return -1; + } + + peer = ccnet_peer_manager_get_peer (mgr, peer_id); + if (!peer) { + peer = ccnet_peer_new (peer_id); + ccnet_peer_manager_add_peer (mgr, peer); + } + + ccnet_peer_manager_add_role (mgr, peer, "MyClient"); + g_object_unref (peer); + return 0; +} + +int +ccnet_rpc_add_role(const char *peer_id, const char *role, GError **error) +{ + CcnetPeerManager *mgr = session->peer_mgr; + CcnetPeer *peer; + + if (!peer_id || strlen(peer_id) != 40) { + g_set_error(error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Peer ID must be of length 40"); + return -1; + } + + if (!role || strlen(role) <= 2) { + g_set_error(error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Invalid role"); + return -1; + } + + peer = ccnet_peer_manager_get_peer (mgr, peer_id); + if (!peer) { + g_set_error(error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "No such peer"); + return -1; + } + ccnet_peer_manager_add_role (mgr, peer, role); + + g_object_unref (peer); + return 0; +} + +int +ccnet_rpc_remove_role(const char *peer_id, const char *role, GError **error) +{ + CcnetPeerManager *mgr = session->peer_mgr; + CcnetPeer *peer; + + if (!peer_id || strlen(peer_id) != 40) { + g_set_error(error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Peer ID must be of length 40"); + return -1; + } + + if (!role || strlen(role) <= 2) { + g_set_error(error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Invalid role"); + return -1; + } + + peer = ccnet_peer_manager_get_peer (mgr, peer_id); + if (!peer) { + g_set_error(error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "No such peer"); + return -1; + } + + ccnet_peer_manager_remove_role (mgr, peer, role); + + g_object_unref (peer); + return 0; +} + +char * +ccnet_rpc_get_config (const char *key, GError **error) +{ + return ccnet_session_config_get_string (session, key); +} + +int +ccnet_rpc_set_config (const char *key, const char *value, GError **error) +{ + return ccnet_session_config_set_string (session, key, value); +} + +char * +ccnet_rpc_pubkey_encrypt (const char *msg_base64, const char *peer_id, GError **error) +{ + unsigned char *msg; + gsize msg_len; + CcnetPeer *peer; + unsigned char *enc_msg; + int enc_msg_len; + char *ret; + + peer = ccnet_peer_manager_get_peer (session->peer_mgr, peer_id); + if (!peer) { + g_warning ("Cannot find peer %s.\n", peer_id); + return NULL; + } + + msg = g_base64_decode (msg_base64, &msg_len); + + enc_msg = public_key_encrypt (peer->pubkey, msg, (int)msg_len, &enc_msg_len); + + ret = g_base64_encode (enc_msg, enc_msg_len); + + g_free (msg); + g_free (enc_msg); + g_object_unref (peer); + return ret; +} + +char * +ccnet_rpc_privkey_decrypt (const char *msg_base64, GError **error) +{ + unsigned char *msg; + gsize msg_len; + unsigned char *dec_msg; + int dec_msg_len; + char *ret; + + msg = g_base64_decode (msg_base64, &msg_len); + + dec_msg = private_key_decrypt (session->privkey, msg, (int)msg_len, &dec_msg_len); + + if (dec_msg_len < 0) { + g_warning ("Failed to decrypt message with RSA priv key.\n"); + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Failed to decrypt"); + g_free (msg); + return NULL; + } + + ret = g_base64_encode (dec_msg, dec_msg_len); + + g_free (msg); + g_free (dec_msg); + return ret; +} + +#ifdef CCNET_SERVER + +#include "user-mgr.h" +#include "group-mgr.h" +#include "org-mgr.h" + + +GList * +ccnet_rpc_list_peer_stat (GError **error) +{ + GList *res = NULL; + GList *ptr, *peer_list; + CcnetPeer *peer; + CcnetPeerManager *peer_mgr = session->peer_mgr; + + peer_list = ccnet_peer_manager_get_peer_list(peer_mgr); + if (peer_list == NULL) { + g_set_error(error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Failed to get peer list"); + return NULL; + } + + ptr = peer_list; + while (ptr) { + peer = ptr->data; + if (peer->is_self) { + ptr = ptr->next; + continue; + } + + guint proc_num = g_hash_table_size (peer->processors); + + CcnetPeerStat* stat = ccnet_peer_stat_new (); + g_object_set (stat, "id", peer->id, + "name", peer->name, + "ip", peer->addr_str, + "encrypt", peer->encrypt_channel, + "last_up", (gint64) peer->last_up, + "proc_num", (int)proc_num, + NULL); + res = g_list_prepend (res, stat); + ptr = ptr->next; + } + g_list_free(peer_list); + + return g_list_reverse (res); +} + + +int +ccnet_rpc_add_emailuser (const char *email, const char *passwd, + int is_staff, int is_active, GError **error) +{ + CcnetUserManager *user_mgr = + ((CcnetServerSession *)session)->user_mgr; + int ret; + + if (!email || !passwd) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Email and passwd can not be NULL"); + return -1; + } + + ret = ccnet_user_manager_add_emailuser (user_mgr, email, passwd, + is_staff, is_active); + + return ret; +} + +int +ccnet_rpc_remove_emailuser (const char *source, const char *email, GError **error) +{ + CcnetUserManager *user_mgr = + ((CcnetServerSession *)session)->user_mgr; + int ret; + + if (!email) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Email can not be NULL"); + return -1; + } + + ret = ccnet_user_manager_remove_emailuser (user_mgr, source, email); + + return ret; +} + +int +ccnet_rpc_validate_emailuser (const char *email, const char *passwd, GError **error) +{ + CcnetUserManager *user_mgr = + ((CcnetServerSession *)session)->user_mgr; + int ret; + + if (!email || !passwd) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Email and passwd can not be NULL"); + return -1; + } + + if (passwd[0] == 0) + return -1; + + ret = ccnet_user_manager_validate_emailuser (user_mgr, email, passwd); + + return ret; +} + +GObject* +ccnet_rpc_get_emailuser (const char *email, GError **error) +{ + CcnetUserManager *user_mgr = + ((CcnetServerSession *)session)->user_mgr; + CcnetEmailUser *emailuser = NULL; + + emailuser = ccnet_user_manager_get_emailuser (user_mgr, email); + + return (GObject *)emailuser; +} + +GObject* +ccnet_rpc_get_emailuser_with_import (const char *email, GError **error) +{ + CcnetUserManager *user_mgr = ((CcnetServerSession *)session)->user_mgr; + CcnetEmailUser *emailuser = NULL; + + emailuser = ccnet_user_manager_get_emailuser_with_import (user_mgr, email); + + return (GObject *)emailuser; +} + +GObject* +ccnet_rpc_get_emailuser_by_id (int id, GError **error) +{ + CcnetUserManager *user_mgr = + ((CcnetServerSession *)session)->user_mgr; + CcnetEmailUser *emailuser = NULL; + + emailuser = ccnet_user_manager_get_emailuser_by_id (user_mgr, id); + + return (GObject *)emailuser; +} + +GList* +ccnet_rpc_get_emailusers (const char *source, + int start, int limit, + const char *status, + GError **error) +{ + CcnetUserManager *user_mgr = + ((CcnetServerSession *)session)->user_mgr; + GList *emailusers = NULL; + + emailusers = ccnet_user_manager_get_emailusers (user_mgr, source, start, limit, status); + + return emailusers; +} + +GList* +ccnet_rpc_search_emailusers (const char *source, + const char *email_patt, + int start, int limit, + GError **error) +{ + CcnetUserManager *user_mgr = + ((CcnetServerSession *)session)->user_mgr; + GList *emailusers = NULL; + + emailusers = ccnet_user_manager_search_emailusers (user_mgr, + source, + email_patt, + start, limit); + + return emailusers; +} + +GList* +ccnet_rpc_search_ldapusers (const char *keyword, + int start, int limit, + GError **error) +{ + GList *ldapusers = NULL; + CcnetUserManager *user_mgr = ((CcnetServerSession *)session)->user_mgr; + + ldapusers = ccnet_user_manager_search_ldapusers (user_mgr, keyword, + start, limit); + return ldapusers; +} + +gint64 +ccnet_rpc_count_emailusers (const char *source, GError **error) +{ + CcnetUserManager *user_mgr = + ((CcnetServerSession *)session)->user_mgr; + + return ccnet_user_manager_count_emailusers (user_mgr, source); +} + +gint64 +ccnet_rpc_count_inactive_emailusers (const char *source, GError **error) +{ + CcnetUserManager *user_mgr = + ((CcnetServerSession *)session)->user_mgr; + + return ccnet_user_manager_count_inactive_emailusers (user_mgr, source); +} + +#if 0 +GList* +ccnet_rpc_filter_emailusers_by_emails (const char *emails, GError **error) +{ + CcnetUserManager *user_mgr = + ((CcnetServerSession *)session)->user_mgr; + + if (!emails || g_strcmp0 (emails, "") == 0) + return NULL; + + return ccnet_user_manager_filter_emailusers_by_emails (user_mgr, emails); +} +#endif + +int +ccnet_rpc_update_emailuser (const char *source, int id, const char* passwd, + int is_staff, int is_active, + GError **error) +{ + CcnetUserManager *user_mgr = + ((CcnetServerSession *)session)->user_mgr; + + return ccnet_user_manager_update_emailuser(user_mgr, source, id, passwd, + is_staff, is_active); +} + +int +ccnet_rpc_update_role_emailuser (const char* email, const char* role, + GError **error) +{ + CcnetUserManager *user_mgr = + ((CcnetServerSession *)session)->user_mgr; + + return ccnet_user_manager_update_role_emailuser(user_mgr, email, role); +} + +GList* +ccnet_rpc_get_superusers (GError **error) +{ + CcnetUserManager *user_mgr = + ((CcnetServerSession *)session)->user_mgr; + + return ccnet_user_manager_get_superusers(user_mgr); +} + +char * +ccnet_rpc_sign_message (const char *message, GError **error) +{ + unsigned char *sig; + unsigned int sig_len; + char *sigret; + + sig = g_new0(unsigned char, RSA_size(session->privkey)); + + if (!RSA_sign (NID_sha1, (const unsigned char *)message, strlen(message), + sig, &sig_len, session->privkey)) { + g_warning ("Failed to sign message: %lu.\n", ERR_get_error()); + return NULL; + } + + sigret = g_base64_encode (sig, sig_len); + g_free (sig); + + return sigret; +} + +int +ccnet_rpc_verify_message (const char *message, + const char *sig_base64, + const char *peer_id, + GError **error) +{ + unsigned char *sig; + gsize sig_len; + CcnetPeer *peer; + + sig = g_base64_decode (sig_base64, &sig_len); + + peer = ccnet_peer_manager_get_peer (session->peer_mgr, peer_id); + if (!peer) { + g_warning ("Cannot find peer %s.\n", peer_id); + return -1; + } + + if (!RSA_verify (NID_sha1, (const unsigned char *)message, strlen(message), + sig, (guint)sig_len, peer->pubkey)) { + g_object_unref (peer); + return -1; + } + + g_object_unref (peer); + return 0; +} + +int +ccnet_rpc_create_group (const char *group_name, const char *user_name, + const char *type, GError **error) +{ + CcnetGroupManager *group_mgr = + ((CcnetServerSession *)session)->group_mgr; + int ret; + + if (!group_name || !user_name) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, + "Group name and user name can not be NULL"); + return -1; + } + + ret = ccnet_group_manager_create_group (group_mgr, group_name, user_name, error); + + return ret; +} + +int +ccnet_rpc_create_org_group (int org_id, const char *group_name, + const char *user_name, GError **error) +{ + CcnetGroupManager *group_mgr = + ((CcnetServerSession *)session)->group_mgr; + int ret; + + if (org_id < 0 || !group_name || !user_name) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad args"); + return -1; + } + + ret = ccnet_group_manager_create_org_group (group_mgr, org_id, + group_name, user_name, error); + + return ret; +} + +int +ccnet_rpc_remove_group (int group_id, GError **error) +{ + CcnetGroupManager *group_mgr = + ((CcnetServerSession *)session)->group_mgr; + int ret; + + if (group_id <= 0) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, + "Invalid group_id parameter"); + return -1; + } + + ret = ccnet_group_manager_remove_group (group_mgr, group_id, error); + + return ret; + +} + +int +ccnet_rpc_group_add_member (int group_id, const char *user_name, + const char *member_name, GError **error) +{ + CcnetGroupManager *group_mgr = + ((CcnetServerSession *)session)->group_mgr; + int ret; + + if (group_id <= 0 || !user_name || !member_name) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, + "Group id and user name and member name can not be NULL"); + return -1; + } + + ret = ccnet_group_manager_add_member (group_mgr, group_id, user_name, member_name, + error); + + return ret; +} + +int +ccnet_rpc_group_remove_member (int group_id, const char *user_name, + const char *member_name, GError **error) +{ + CcnetGroupManager *group_mgr = + ((CcnetServerSession *)session)->group_mgr; + int ret; + + if (!user_name || !member_name) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, + "User name and member name can not be NULL"); + return -1; + } + + ret = ccnet_group_manager_remove_member (group_mgr, group_id, user_name, + member_name, error); + + return ret; +} + +int +ccnet_rpc_group_set_admin (int group_id, const char *member_name, + GError **error) +{ + CcnetGroupManager *group_mgr = + ((CcnetServerSession *)session)->group_mgr; + int ret; + + if (group_id <= 0 || !member_name) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, + "Bad arguments"); + return -1; + } + + ret = ccnet_group_manager_set_admin (group_mgr, group_id, member_name, + error); + return ret; +} + +int +ccnet_rpc_group_unset_admin (int group_id, const char *member_name, + GError **error) +{ + CcnetGroupManager *group_mgr = + ((CcnetServerSession *)session)->group_mgr; + int ret; + + if (group_id <= 0 || !member_name) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, + "Bad arguments"); + return -1; + } + + ret = ccnet_group_manager_unset_admin (group_mgr, group_id, member_name, + error); + return ret; +} + +int +ccnet_rpc_set_group_name (int group_id, const char *group_name, + GError **error) +{ + CcnetGroupManager *group_mgr = + ((CcnetServerSession *)session)->group_mgr; + int ret; + + if (group_id <= 0 || !group_name) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, + "Bad arguments"); + return -1; + } + + ret = ccnet_group_manager_set_group_name (group_mgr, group_id, group_name, + error); + return ret; +} + +int +ccnet_rpc_quit_group (int group_id, const char *user_name, GError **error) +{ + CcnetGroupManager *group_mgr = + ((CcnetServerSession *)session)->group_mgr; + int ret; + + if (group_id <= 0 || !user_name) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, + "Group id and user name can not be NULL"); + return -1; + } + + ret = ccnet_group_manager_quit_group (group_mgr, group_id, user_name, error); + + return ret; +} + +static GList * +convert_group_list (CcnetGroupManager *mgr, GList *group_ids) +{ + GList *ret = NULL, *ptr; + + for (ptr = group_ids; ptr; ptr = ptr->next) { + int group_id = (int)(long)ptr->data; + CcnetGroup *group = ccnet_group_manager_get_group (mgr, group_id, + NULL); + if (group != NULL) { + ret = g_list_prepend (ret, group); + } + } + + return g_list_reverse (ret); +} + +GList * +ccnet_rpc_get_groups (const char *username, GError **error) +{ + CcnetGroupManager *group_mgr = + ((CcnetServerSession *)session)->group_mgr; + GList *group_ids = NULL; + GList *ret = NULL; + + if (!username) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, + "User name can not be NULL"); + return NULL; + } + + group_ids = ccnet_group_manager_get_groupids_by_user (group_mgr, username, + error); + if (group_ids == NULL) { + return NULL; + } + + ret = convert_group_list (group_mgr, group_ids); + g_list_free (group_ids); + + return ret; +} + +GList * +ccnet_rpc_get_all_groups (int start, int limit, + const char *source, GError **error) +{ + CcnetGroupManager *group_mgr = + ((CcnetServerSession *)session)->group_mgr; + GList *ret = NULL; + + ret = ccnet_group_manager_get_all_groups (group_mgr, start, limit, error); + + return ret; +} + +GObject * +ccnet_rpc_get_group (int group_id, GError **error) +{ + CcnetGroupManager *group_mgr = + ((CcnetServerSession *)session)->group_mgr; + CcnetGroup *group = NULL; + + group = ccnet_group_manager_get_group (group_mgr, group_id, error); + if (!group) { + return NULL; + } + + /* g_object_ref (group); */ + return (GObject *)group; +} + + +GList * +ccnet_rpc_get_group_members (int group_id, GError **error) +{ + CcnetGroupManager *group_mgr = + ((CcnetServerSession *)session)->group_mgr; + GList *ret = NULL; + + ret = ccnet_group_manager_get_group_members (group_mgr, group_id, error); + if (ret == NULL) + return NULL; + + return g_list_reverse (ret); +} + +int +ccnet_rpc_check_group_staff (int group_id, const char *user_name, + GError **error) +{ + CcnetGroupManager *group_mgr = + ((CcnetServerSession *)session)->group_mgr; + + if (group_id <= 0 || !user_name) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, + "Bad arguments"); + return -1; + } + + return ccnet_group_manager_check_group_staff (group_mgr, + group_id, user_name); +} + +int +ccnet_rpc_remove_group_user (const char *user, GError **error) +{ + CcnetGroupManager *group_mgr = ((CcnetServerSession *)session)->group_mgr; + if (!user) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return -1; + } + + return ccnet_group_manager_remove_group_user (group_mgr, user); +} + +int +ccnet_rpc_is_group_user (int group_id, const char *user, GError **error) +{ + CcnetGroupManager *group_mgr = ((CcnetServerSession *)session)->group_mgr; + if (!user || group_id < 0) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return 0; + } + + return ccnet_group_manager_is_group_user (group_mgr, group_id, user); +} + +int +ccnet_rpc_set_group_creator (int group_id, const char *user_name, + GError **error) +{ + CcnetGroupManager *group_mgr = ((CcnetServerSession *)session)->group_mgr; + if (!user_name || group_id < 0) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return -1; + } + + return ccnet_group_manager_set_group_creator (group_mgr, group_id, + user_name); +} + +int +ccnet_rpc_create_org (const char *org_name, const char *url_prefix, + const char *creator, GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + + if (!org_name || !url_prefix || !creator) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return -1; + } + + return ccnet_org_manager_create_org (org_mgr, org_name, url_prefix, creator, + error); +} + +int +ccnet_rpc_remove_org (int org_id, GError **error) +{ + GList *group_ids = NULL, *email_list=NULL, *ptr; + const char *url_prefix = NULL; + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + CcnetUserManager *user_mgr = ((CcnetServerSession *)session)->user_mgr; + CcnetGroupManager *group_mgr = ((CcnetServerSession *)session)->group_mgr; + + if (org_id < 0) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return -1; + } + + url_prefix = ccnet_org_manager_get_url_prefix_by_org_id (org_mgr, org_id, + error); + email_list = ccnet_org_manager_get_org_emailusers (org_mgr, url_prefix, + 0, INT_MAX); + ptr = email_list; + while (ptr) { + ccnet_user_manager_remove_emailuser (user_mgr, "DB", (gchar *)ptr->data); + ptr = ptr->next; + } + string_list_free (email_list); + + group_ids = ccnet_org_manager_get_org_groups (org_mgr, org_id, 0, INT_MAX); + ptr = group_ids; + while (ptr) { + ccnet_group_manager_remove_group (group_mgr, (int)(long)ptr->data, error); + ptr = ptr->next; + } + g_list_free (group_ids); + + return ccnet_org_manager_remove_org (org_mgr, org_id, error); +} + +GList * +ccnet_rpc_get_all_orgs (int start, int limit, GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + GList *ret = NULL; + + ret = ccnet_org_manager_get_all_orgs (org_mgr, start, limit); + + return ret; +} + +gint64 +ccnet_rpc_count_orgs (GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + + return ccnet_org_manager_count_orgs(org_mgr); +} + + +GObject * +ccnet_rpc_get_org_by_url_prefix (const char *url_prefix, GError **error) +{ + CcnetOrganization *org = NULL; + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + + if (!url_prefix) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return NULL; + } + + org = ccnet_org_manager_get_org_by_url_prefix (org_mgr, url_prefix, error); + if (!org) + return NULL; + + return (GObject *)org; +} + +GObject * +ccnet_rpc_get_org_by_id (int org_id, GError **error) +{ + CcnetOrganization *org = NULL; + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + + if (org_id <= 0) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return NULL; + } + + org = ccnet_org_manager_get_org_by_id (org_mgr, org_id, error); + if (!org) + return NULL; + + return (GObject *)org; +} + +int +ccnet_rpc_add_org_user (int org_id, const char *email, int is_staff, + GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + + if (org_id < 0 || !email) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return -1; + } + + return ccnet_org_manager_add_org_user (org_mgr, org_id, email, is_staff, + error); +} + +int +ccnet_rpc_remove_org_user (int org_id, const char *email, GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + + if (org_id < 0 || !email) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return -1; + } + + return ccnet_org_manager_remove_org_user (org_mgr, org_id, email, error); +} + +GList * +ccnet_rpc_get_orgs_by_user (const char *email, GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + GList *org_list = NULL; + + org_list = ccnet_org_manager_get_orgs_by_user (org_mgr, email, error); + + return org_list; +} + +GList * +ccnet_rpc_get_org_emailusers (const char *url_prefix, int start , int limit, + GError **error) +{ + CcnetUserManager *user_mgr = ((CcnetServerSession *)session)->user_mgr; + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + GList *email_list = NULL, *ptr; + GList *ret = NULL; + + if (!url_prefix) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return NULL; + } + + email_list = ccnet_org_manager_get_org_emailusers (org_mgr, url_prefix, + start, limit); + if (email_list == NULL) { + return NULL; + } + + ptr = email_list; + while (ptr) { + char *email = ptr->data; + CcnetEmailUser *emailuser = ccnet_user_manager_get_emailuser (user_mgr, + email); + if (emailuser != NULL) { + ret = g_list_prepend (ret, emailuser); + } + + ptr = ptr->next; + } + + string_list_free (email_list); + + return g_list_reverse (ret); +} + +int +ccnet_rpc_add_org_group (int org_id, int group_id, GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + + if (org_id < 0 || group_id < 0) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return -1; + } + + return ccnet_org_manager_add_org_group (org_mgr, org_id, group_id, error); +} + +int +ccnet_rpc_remove_org_group (int org_id, int group_id, GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + + if (org_id < 0 || group_id < 0) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return -1; + } + + return ccnet_org_manager_remove_org_group (org_mgr, org_id, group_id, + error); +} + +int +ccnet_rpc_is_org_group (int group_id, GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + + if (group_id <= 0) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return -1; + } + + return ccnet_org_manager_is_org_group (org_mgr, group_id, error); +} + +int +ccnet_rpc_get_org_id_by_group (int group_id, GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + + if (group_id <= 0) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return -1; + } + + return ccnet_org_manager_get_org_id_by_group (org_mgr, group_id, error); +} + +GList * +ccnet_rpc_get_org_groups (int org_id, int start, int limit, GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + CcnetGroupManager *group_mgr = ((CcnetServerSession *)session)->group_mgr; + GList *group_ids = NULL; + GList *ret = NULL; + + if (org_id < 0) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return NULL; + } + + /* correct parameter */ + if (start < 0 ) { + start = 0; + } + + group_ids = ccnet_org_manager_get_org_groups (org_mgr, org_id, start, + limit); + if (group_ids == NULL) + return NULL; + + ret = convert_group_list (group_mgr, group_ids); + g_list_free (group_ids); + + return ret; +} + +int +ccnet_rpc_org_user_exists (int org_id, const char *email, GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + + if (org_id < 0 || !email) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return -1; + } + + return ccnet_org_manager_org_user_exists (org_mgr, org_id, email, error); +} + +int +ccnet_rpc_is_org_staff (int org_id, const char *email, GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + + if (org_id < 0 || !email) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return -1; + } + + return ccnet_org_manager_is_org_staff (org_mgr, org_id, email, error); +} + +int +ccnet_rpc_set_org_staff (int org_id, const char *email, GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + + if (org_id < 0 || !email) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return -1; + } + + return ccnet_org_manager_set_org_staff (org_mgr, org_id, email, error); +} + +int +ccnet_rpc_unset_org_staff (int org_id, const char *email, GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + + if (org_id < 0 || !email) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return -1; + } + + return ccnet_org_manager_unset_org_staff (org_mgr, org_id, email, error); +} + +int +ccnet_rpc_set_org_name (int org_id, const char *org_name, GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)session)->org_mgr; + + if (org_id < 0 || !org_name) { + g_set_error (error, CCNET_DOMAIN, CCNET_ERR_INTERNAL, "Bad arguments"); + return -1; + } + + return ccnet_org_manager_set_org_name (org_mgr, org_id, org_name, error); +} + + +#endif /* CCNET_SERVER */ diff --git a/net/common/rpc-service.h b/net/common/rpc-service.h new file mode 100644 index 0000000..a2d10cc --- /dev/null +++ b/net/common/rpc-service.h @@ -0,0 +1,318 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef RPC_SERVICE_H +#define RPC_SERVICE_H + +struct CcnetSession; + +void ccnet_start_rpc(CcnetSession *session); + +char *ccnet_rpc_list_peers(GError **error); +GList *ccnet_rpc_list_resolving_peers (GError **error); + + +GList* ccnet_rpc_get_peers_by_role(const char *role, GError **error); + + +GObject *ccnet_rpc_get_peer(const char *peerid, GError **error); + +int +ccnet_rpc_update_peer_address(const char *peer_id, const char *addr, + int port, GError **error); + +GObject *ccnet_rpc_get_peer_by_idname(const char *idname, GError **error); + +char * +ccnet_rpc_list_users(GError **error); + +GObject * +ccnet_rpc_get_user(const char *userid, GError **error); + + +GObject * +ccnet_rpc_get_user_of_peer(const char *peerid, GError **error); + + +GObject *ccnet_rpc_get_session_info(GError **error); + +int +ccnet_rpc_add_client(const char *user_id, GError **error); + +int +ccnet_rpc_add_role(const char *user_id, const char *role, GError **error); + +int +ccnet_rpc_remove_role(const char *user_id, const char *role, GError **error); + + +GList *ccnet_rpc_get_events(int offset, int limit, GError **error); +int ccnet_rpc_count_event (GError **error); + + +/** + * ccnet_get_config: + * + * Return the config value with key @key + */ +char * +ccnet_rpc_get_config (const char *key, GError **error); + +/** + * ccnet_rpc_set_config: + * + * Set the value of config item with key @key to @value + */ +int +ccnet_rpc_set_config (const char *key, const char *value, GError **error); + + +/** + * ccnet_rpc_upload_profile: + * + * Upload profile to the relay who is @relay_id + */ +int +ccnet_rpc_upload_profile (const char *relay_id, GError **error); + +char * +ccnet_rpc_pubkey_encrypt (const char *msg_base64, + const char *peer_id, + GError **error); + +char * +ccnet_rpc_privkey_decrypt (const char *msg_base64, GError **error); + +#ifdef CCNET_SERVER + +GList * +ccnet_rpc_list_peer_stat (GError **error); + +int +ccnet_rpc_add_emailuser (const char *email, const char *passwd, + int is_staff, int is_active, GError **error); + +int +ccnet_rpc_remove_emailuser (const char *source, const char *email, GError **error); + +int +ccnet_rpc_validate_emailuser (const char *email, const char *passwd, GError **error); + +GObject* +ccnet_rpc_get_emailuser (const char *email, GError **error); + +GObject* +ccnet_rpc_get_emailuser_with_import (const char *email, GError **error); + +GObject* +ccnet_rpc_get_emailuser_by_id (int id, GError **error); + +GList* +ccnet_rpc_get_emailusers (const char *source, int start, int limit, const char *status, GError **error); + +GList* +ccnet_rpc_search_emailusers (const char *source, + const char *email_patt, + int start, int limit, + GError **error); + +GList* +ccnet_rpc_search_ldapusers (const char *keyword, + int start, int limit, + GError **error); + +/* Get total counts of email users. */ +gint64 +ccnet_rpc_count_emailusers (const char *source, GError **error); + +gint64 +ccnet_rpc_count_inactive_emailusers (const char *source, GError **error); + +/** + * Select multiple users according to the given emails. + * + * @emails: emails seperated by ",", e.g., "foo@foo.com, bar@bar.com" + */ +GList* +ccnet_rpc_filter_emailusers_by_emails (const char *emails, GError **error); + +int +ccnet_rpc_update_emailuser (const char *source, int id, const char* passwd, + int is_staff, int is_active, + GError **error); + +int +ccnet_rpc_update_role_emailuser (const char* email, const char* role, GError **error); + +GList* +ccnet_rpc_get_superusers (GError **error); + +int +ccnet_rpc_add_binding (const char *email, const char *peer_id, GError **error); + +char * +ccnet_rpc_get_binding_email (const char *peer_id, GError **error); + +char * +ccnet_rpc_get_binding_peerids (const char *email, GError **error); + +int +ccnet_rpc_remove_binding (const char *email, GError **error); + +int +ccnet_rpc_remove_one_binding (const char *email, const char *peer_id, + GError **error); + +GList * +ccnet_rpc_get_peers_by_email (const char *email, GError **error); + +char * +ccnet_rpc_sign_message (const char *message, GError **error); + +int +ccnet_rpc_verify_message (const char *message, + const char *sig_base64, + const char *peer_id, + GError **error); + +int +ccnet_rpc_create_group (const char *group_name, const char *user_name, + const char *type, GError **error); + +int +ccnet_rpc_create_org_group (int org_id, const char *group_name, + const char *user_name, GError **error); + +int +ccnet_rpc_remove_group (int group_id, GError **error); + +int +ccnet_rpc_group_add_member (int group_id, const char *user_name, + const char *member_name, GError **error); +int +ccnet_rpc_group_remove_member (int group_id, const char *user_name, + const char *member_name, GError **error); + +int +ccnet_rpc_group_set_admin (int group_id, const char *member_name, + GError **error); + +int +ccnet_rpc_group_unset_admin (int group_id, const char *member_name, + GError **error); + +int +ccnet_rpc_set_group_name (int group_id, const char *group_name, + GError **error); + +int +ccnet_rpc_quit_group (int group_id, const char *user_name, GError **error); + +GList * +ccnet_rpc_get_groups (const char *username, GError **error); + +GList * +ccnet_rpc_get_all_groups (int start, int limit, const char *source, GError **error); + +GObject * +ccnet_rpc_get_group (int group_id, GError **error); + +GList * +ccnet_rpc_get_group_members (int group_id, GError **error); + +int +ccnet_rpc_check_group_staff (int group_id, const char *user_name, + GError **error); + +int +ccnet_rpc_remove_group_user (const char *user, GError **error); + +int +ccnet_rpc_is_group_user (int group_id, const char *user, GError **error); + +int +ccnet_rpc_set_group_creator (int group_id, const char *user_name, + GError **error); + +int +ccnet_rpc_create_org (const char *org_name, const char *url_prefix, + const char *creator, GError **error); + +int +ccnet_rpc_remove_org (int org_id, GError **error); + +GList * +ccnet_rpc_get_all_orgs (int start, int limit, GError **error); + +gint64 +ccnet_rpc_count_orgs (GError **error); + +GObject * +ccnet_rpc_get_org_by_url_prefix (const char *url_prefix, GError **error); + +GObject * +ccnet_rpc_get_org_by_id (int org_id, GError **error); + +int +ccnet_rpc_add_org_user (int org_id, const char *email, int is_staff, + GError **error); + +int +ccnet_rpc_remove_org_user (int org_id, const char *email, GError **error); + +GList * +ccnet_rpc_get_orgs_by_user (const char *email, GError **error); + +GList * +ccnet_rpc_get_org_emailusers (const char *url_prefix, int start , int limit, + GError **error); + +int +ccnet_rpc_add_org_group (int org_id, int group_id, GError **error); + +int +ccnet_rpc_remove_org_group (int org_id, int group_id, GError **error); + +int +ccnet_rpc_is_org_group (int group_id, GError **error); + +int +ccnet_rpc_get_org_id_by_group (int group_id, GError **error); + +GList * +ccnet_rpc_get_org_groups (int org_id, int start, int limit, GError **error); + +int +ccnet_rpc_org_user_exists (int org_id, const char *email, GError **error); + +int +ccnet_rpc_is_org_staff (int org_id, const char *email, GError **error); + +int +ccnet_rpc_set_org_staff (int org_id, const char *email, GError **error); + +int +ccnet_rpc_unset_org_staff (int org_id, const char *email, GError **error); + +int +ccnet_rpc_set_org_name (int org_id, const char *org_name, GError **error); + +#endif /* CCNET_SERVER */ + +/** + * ccnet_rpc_login_to_relay: + * + * send email/passwd info to relay("after registration"), to get a "MyClient" role on relay + */ +int +ccnet_rpc_login_relay (const char *relay_id, const char *email, + const char *passwd, GError **error); + +/** + * ccnet_rpc_logout_to_relay: + * + * ask the relay to delete i) 'MyClient' info, ii) previous binding to an email + */ +int +ccnet_rpc_logout_relay (const char *relay_id, GError **error); + +#endif /* RPC_SERVICE_H */ diff --git a/net/common/session.c b/net/common/session.c new file mode 100644 index 0000000..6caedda --- /dev/null +++ b/net/common/session.c @@ -0,0 +1,628 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include +#include +#include +#include + +#include "getgateway.h" +#include "utils.h" +#include "net.h" +#include "rsa.h" +#include "ccnet-config.h" + +#include "session.h" +#include "peer.h" +#include "peer-mgr.h" +#include "perm-mgr.h" +#include "packet-io.h" +#include "connect-mgr.h" +#include "message.h" +#include "message-manager.h" +#include "algorithms.h" +#include "proc-factory.h" + +#define DEBUG_FLAG CCNET_DEBUG_OTHER +#include "log.h" + +#define THREAD_POOL_SIZE 50 + +static void ccnet_service_free (CcnetService *service); + + +G_DEFINE_TYPE (CcnetSession, ccnet_session, CCNET_TYPE_SESSION_BASE); + +static void on_peer_auth_done (CcnetPeerManager *manager, + CcnetPeer *peer, gpointer user_data); + +static void +ccnet_session_class_init (CcnetSessionClass *klass) +{ + /* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */ +} + +static void +ccnet_session_init (CcnetSession *session) +{ + /* note, the order is important. */ + session->service_hash = g_hash_table_new_full ( + g_str_hash, g_str_equal, + g_free, (GDestroyNotify)ccnet_service_free); + session->proc_factory = ccnet_proc_factory_new (session); + session->peer_mgr = ccnet_peer_manager_new (session); + session->connMgr = ccnet_conn_manager_new (session); + session->msg_mgr = ccnet_message_manager_new (session); + session->perm_mgr = ccnet_perm_manager_new (session); + session->job_mgr = ccnet_job_manager_new (THREAD_POOL_SIZE); +} + +static int load_rsakey(CcnetSession *session) +{ + char *path; + FILE *fp; + RSA *key; + + path = g_build_filename(session->config_dir, PEER_KEYFILE, NULL); + if (!g_file_test(path, G_FILE_TEST_EXISTS)) + ccnet_error ("Can't load rsa private key from %s\n", path); + if ((fp = g_fopen(path, "rb")) == NULL) + ccnet_error ("Can't open private key file %s: %s\n", path, + strerror(errno)); + if ((key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL)) == NULL) + ccnet_error ("Can't open load key file %s: format error\n", path); + fclose(fp); + + session->privkey = key; + session->pubkey = private_key_to_pub(key); + g_free(path); + + return 0; +} + +static void listen_on_localhost (CcnetSession *session); +static void listen_on_pipe (CcnetSession *session); +static void save_pubinfo (CcnetSession *session); + +CcnetSession * +ccnet_session_new () +{ + return g_object_new (CCNET_TYPE_SESSION, NULL); +} + +int +ccnet_session_load_config (CcnetSession *session, + const char *central_config_dir_r, + const char *config_dir_r) +{ + int ret = 0; + char *config_file = NULL, *config_dir = NULL, *central_config_dir = NULL; + char *id = NULL, *name = NULL, *port_str = NULL, + *lport_str = NULL, *un_path = NULL, + *user_name = NULL; +#ifdef CCNET_SERVER + char *service_url; +#endif + int port, local_port = 0; + unsigned char sha1[20]; + GKeyFile *key_file; + + config_dir = ccnet_expand_path (config_dir_r); + + if (checkdir(config_dir) < 0) { + ccnet_error ("Config dir %s does not exist or is not " + "a directory.\n", config_dir); + return -1; + } + +#ifdef CCNET_SERVER + if (central_config_dir_r) { + central_config_dir = ccnet_expand_path (central_config_dir_r); + if (checkdir(central_config_dir) < 0) { + ccnet_error ("Server Config dir %s does not exist or is not " + "a directory.\n", central_config_dir); + return -1; + } + } +#endif + + config_file = g_build_filename (central_config_dir ? central_config_dir : config_dir, + SESSION_CONFIG_FILENAME, NULL); + ccnet_message ("using config file %s\n", config_file); + key_file = g_key_file_new (); + g_key_file_set_list_separator (key_file, ','); + if (!g_key_file_load_from_file (key_file, config_file, + G_KEY_FILE_KEEP_COMMENTS, NULL)) + { + ccnet_warning ("Can't load config file %s.\n", config_file); + return -1; + } + + id = ccnet_key_file_get_string (key_file, "General", "ID"); + user_name = ccnet_key_file_get_string (key_file, "General", "USER_NAME"); + name = ccnet_key_file_get_string (key_file, "General", "NAME"); +#ifdef CCNET_SERVER + service_url = ccnet_key_file_get_string (key_file, "General", "SERVICE_URL"); +#endif + port_str = ccnet_key_file_get_string (key_file, "Network", "PORT"); + + lport_str = ccnet_key_file_get_string (key_file, "Client", "PORT"); + un_path = ccnet_key_file_get_string (key_file, "Client", "UNIX_SOCKET"); + + if (port_str == NULL) { + port = 0; + } else { + port = atoi (port_str); + if (port <= 0 || port > 65535) { + port = DEFAULT_PORT; + } + } + + if ( (id == NULL) || (strlen (id) != SESSION_ID_LENGTH) + || (hex_to_sha1 (id, sha1) < 0) ) { + ccnet_error ("Wrong ID\n"); + ret = -1; + goto onerror; + } + + if (lport_str) + local_port = atoi (lport_str); + + memcpy (session->base.id, id, 40); + session->base.id[40] = '\0'; + session->base.name = g_strdup(name); + session->base.user_name = g_strdup(user_name); + session->base.public_port = port; +#ifdef CCNET_SERVER + session->base.service_url = g_strdup(service_url); +#endif + session->config_file = config_file; + session->config_dir = config_dir; + session->central_config_dir = central_config_dir; + session->un_path = un_path; + session->local_port = local_port; + session->keyf = key_file; + + load_rsakey(session); + + ret = 0; + +onerror: + g_free (id); + g_free (name); + g_free (user_name); + g_free (port_str); + g_free (lport_str); +#ifdef CCNET_SERVER + g_free (service_url); +#endif + return ret; +} + +void +ccnet_session_free (CcnetSession *session) +{ + ccnet_peer_manager_free (session->peer_mgr); + + g_object_unref (session); +} + + +int +ccnet_session_prepare (CcnetSession *session, + const char *central_config_dir, + const char *config_dir_r, + gboolean test_config) +{ + char *misc_path; + int ret; + + if (ccnet_session_load_config (session, central_config_dir, config_dir_r) < 0) + return -1; + + misc_path = g_build_filename (session->config_dir, "misc", NULL); + if (checkdir_with_mkdir (misc_path) < 0) { + ccnet_error ("mkdir %s error", misc_path); + return -1; + } + + /* config db */ + session->config_db = ccnet_session_config_open_db (misc_path); + if (!session->config_db) { + ccnet_warning ("Failed to open config db.\n"); + return -1; + } + + /* call subclass prepare */ + ret = CCNET_SESSION_GET_CLASS (session)->prepare(session); + if (ret < 0) + return ret; + + /* peer */ + ccnet_peer_manager_prepare(session->peer_mgr); + g_signal_connect (session->peer_mgr, "peer-auth-done", + G_CALLBACK(on_peer_auth_done), session); + + /* permission manager */ + ccnet_perm_manager_prepare (session->perm_mgr); + + g_free (misc_path); + + if (test_config) { + return 0; + } else { + /* Open localhost, if failed, then the program will exists. This is used + * to prevent two instance of ccnet on the same port. + */ +#ifdef WIN32 + listen_on_localhost (session); +#else + listen_on_pipe (session); +#endif + + /* refresh pubinfo on every startup */ + save_pubinfo (session); + } + + return 0; +} + + +static void +save_peerinfo (CcnetSession *session) +{ + FILE *fp; + char *path; + char filename[64]; + + sprintf(filename, "%s%s", session->base.id, ".peer"); + path = g_build_filename (session->config_dir, "misc", filename, NULL); + + if ((fp = g_fopen(path, "wb")) == NULL) { + ccnet_warning ("Open public info file %s error: %s\n", + path, strerror(errno)); + g_free (path); + return; + } + g_free (path); + + ccnet_message ("Update pubinfo file\n"); + session->myself->public_port = session->base.public_port; + + GString *str = ccnet_peer_to_string (session->myself); + fputs (str->str, fp); + fclose (fp); + g_string_free (str, TRUE); +} + + +static void save_pubinfo (CcnetSession *session) +{ + save_peerinfo(session); +} + +void +ccnet_session_save_config (CcnetSession *session) +{ + GError *error = NULL; + char *str; + FILE *fp; + + ccnet_message ("[Session] Saving configure file\n"); + if (session->saving_pub) { + /* only update timestamp when pubinfo changes */ + save_pubinfo (session); + session->saving_pub = 0; + } + + g_key_file_set_string (session->keyf, "General", "NAME", + session->base.name); + g_key_file_set_string (session->keyf, "General", "ID", + session->base.id); + g_key_file_set_string (session->keyf, "General", "USER_NAME", + session->base.user_name); +#ifdef CCNET_SERVER + g_key_file_set_string (session->keyf, "General", "SERVICE_URL", + session->base.service_url?session->base.service_url:""); +#endif + g_key_file_set_integer (session->keyf, "Network", "PORT", + session->base.public_port); + + g_key_file_set_integer (session->keyf, "Client", "PORT", session->local_port); + + str = g_key_file_to_data (session->keyf, NULL, &error); + if (error) { + ccnet_warning ("Can't save unauth peer info: %s\n", + error->message); + return; + } + + if ((fp = g_fopen (session->config_file, "wb")) == NULL) { + ccnet_warning ("Can't save session conf: %s\n", strerror(errno)); + g_free (str); + return; + } + + fputs (str, fp); + fclose (fp); + + g_free (str); + return; +} + +void +ccnet_session_save (CcnetSession *session) +{ + /* ccnet_req_manager_backup_requirements (session->reqMgr); */ +} + +void +ccnet_session_on_exit (CcnetSession *session) +{ + time_t t; + + ccnet_peer_manager_on_exit (session->peer_mgr); + ccnet_session_save (session); + + t = time(NULL); + ccnet_message ("Exit at %s\n", ctime(&t)); + ccnet_session_free (session); +} + + +static const char *net_status_string (int status) +{ + switch (status) { + case NET_STATUS_DOWN: + return "Down"; + case NET_STATUS_INNAT: + return "In nat"; + case NET_STATUS_FULL: + return "Full"; + default: + return "Unknown"; + } +} + +static void accept_local_client (evutil_socket_t fd, short event, void *vsession) +{ + CcnetSession *session = vsession; + CcnetPacketIO *io; + int connfd; + CcnetPeer *peer; + static int local_id = 0; + + connfd = accept (fd, NULL, 0); + + ccnet_message ("Accepted a local client\n"); + + io = ccnet_packet_io_new_incoming (session, NULL, connfd); + peer = ccnet_peer_new (session->base.id); + peer->name = g_strdup_printf("local-%d", local_id++); + peer->is_local = TRUE; + ccnet_peer_set_io (peer, io); + ccnet_peer_set_net_state (peer, PEER_CONNECTED); + ccnet_peer_manager_add_local_peer (session->peer_mgr, peer); + g_object_unref (peer); +} + +static void listen_on_localhost (CcnetSession *session) +{ + int sockfd; + + if ( (sockfd = ccnet_net_bind_v4 ("127.0.0.1", &session->local_port)) < 0) { + printf ("listen on localhost failed\n"); + exit (1); + } + ccnet_message ("Listen on 127.0.0.1 %d\n", session->local_port); + + listen (sockfd, 5); + event_set (&session->local_event, sockfd, EV_READ | EV_PERSIST, + accept_local_client, session); + event_add (&session->local_event, NULL); +} + +#ifndef WIN32 + +static void listen_on_pipe (CcnetSession *session) +{ + int pipe_fd = socket (AF_UNIX, SOCK_STREAM, 0); + char *un_path = NULL; + if (pipe_fd < 0) { + ccnet_warning ("Failed to create unix socket fd : %s\n", + strerror(errno)); + goto failed; + } + + struct sockaddr_un saddr; + saddr.sun_family = AF_UNIX; + + if (!session->un_path) + un_path = g_build_filename (session->config_dir, CCNET_PIPE_NAME, NULL); + else + un_path = g_strdup(session->un_path); + + if (strlen(un_path) > sizeof(saddr.sun_path)-1) { + ccnet_warning ("Unix socket path %s is too long." + "Please set or modify UNIX_SOCKET option in ccnet.conf.\n", + un_path); + g_free (un_path); + goto failed; + } + + if (g_file_test (un_path, G_FILE_TEST_EXISTS)) { + ccnet_warning ("socket file exists, delete it anyway\n"); + if (g_unlink (un_path) < 0) { + ccnet_warning ("delete socket file failed : %s\n", strerror(errno)); + goto failed; + } + } + + g_strlcpy (saddr.sun_path, un_path, sizeof(saddr.sun_path)); + if (bind(pipe_fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) { + ccnet_warning ("failed to bind unix socket fd to %s : %s\n", + un_path, strerror(errno)); + goto failed; + } + + if (listen(pipe_fd, 3) < 0) { + ccnet_warning ("failed to listen to unix socket: %s\n", strerror(errno)); + goto failed; + } + + if (chmod(un_path, 0700) < 0) { + ccnet_warning ("failed to set permisson for unix socket %s: %s\n", + un_path, strerror(errno)); + goto failed; + } + + event_set (&session->local_pipe_event, pipe_fd, EV_READ | EV_PERSIST, + accept_local_client, session); + event_add (&session->local_pipe_event, NULL); + + ccnet_message ("Listen on %s for local clients\n", un_path); + + g_free (un_path); + + return; + +failed: + ccnet_warning ("listen on unix socket failed\n"); + exit (1); +} + +#endif // WIN32 + + +void +ccnet_session_start_network (CcnetSession *session) +{ + session->base.net_status = NET_STATUS_FULL; + ccnet_conn_manager_start (session->connMgr); + session->myself->addr_str = NULL; +} + +void +ccnet_session_shutdown_network (CcnetSession *session) +{ + GList *peers, *ptr; + + peers = ccnet_peer_manager_get_peer_list(session->peer_mgr); + for (ptr = peers; ptr; ptr = ptr->next) + ccnet_peer_shutdown ((CcnetPeer *)ptr->data); + g_list_free (peers); + + ccnet_conn_manager_stop (session->connMgr); +} + +static int +restart_network (CcnetSession *session) +{ + ccnet_session_start_network (session); + if (session->base.net_status != NET_STATUS_DOWN) { + session->start_failure = 0; + return FALSE; + } + + if (++session->start_failure > 3) + return FALSE; + + return TRUE; +} + +void +ccnet_session_start (CcnetSession *session) +{ + ccnet_proc_factory_start (session->proc_factory); + ccnet_message_manager_start (session->msg_mgr); + + if (session->base.public_port == 0) { + return; + } + + ccnet_session_start_network (session); + if (session->base.net_status == NET_STATUS_DOWN) { + ccnet_timer_new ((TimerCB)restart_network, session, 10000); + } + + ccnet_peer_manager_start (session->peer_mgr); +} + + +static void +ccnet_service_free (CcnetService *service) +{ + g_free (service->svc_name); + g_free (service); +} + +int +ccnet_session_register_service (CcnetSession *session, + const char *svc_name, + const char *group, + CcnetPeer *peer) +{ + CcnetService *service = g_new0 (CcnetService, 1); + + if (g_hash_table_lookup (session->service_hash, svc_name)) { + ccnet_debug ("[Service] Service %s has already been registered\n", + svc_name); + return -1; + } + ccnet_debug ("[Service] Service %s registered\n", svc_name); + + ccnet_perm_manager_register_service (session->perm_mgr, svc_name, + group, peer); + + service->svc_name = g_strdup(svc_name); + service->provider = peer; + + g_hash_table_insert (session->service_hash, g_strdup(svc_name), service); + + return 0; +} + +CcnetService* +ccnet_session_get_service (CcnetSession *session, + const char *svc_name) +{ + return g_hash_table_lookup (session->service_hash, svc_name); +} + +gboolean remove_service_cmp (gpointer key, + gpointer value, + gpointer user_data) +{ + CcnetService *service = value; + + if (service->provider == user_data) { + ccnet_debug ("[Service] Service %s un-registered\n", (char *)key); + return TRUE; + } + return FALSE; +} + +void +ccnet_session_unregister_service (CcnetSession *session, + CcnetPeer *peer) +{ + g_hash_table_foreach_remove (session->service_hash, + remove_service_cmp, peer); +} + + +static void on_peer_auth_done (CcnetPeerManager *manager, + CcnetPeer *peer, gpointer user_data) +{ + CcnetSession *session = (CcnetSession *)user_data; + + CCNET_SESSION_GET_CLASS (session)->on_peer_auth_done(session, peer); +} + + +gboolean +ccnet_session_should_encrypt_channel (CcnetSession *session) +{ + return session->encrypt_channel; +} diff --git a/net/common/session.h b/net/common/session.h new file mode 100644 index 0000000..40c76bc --- /dev/null +++ b/net/common/session.h @@ -0,0 +1,141 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_SESSION_H +#define CCNET_SESSION_H + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#include +#include +#else +#include +#endif + +#include +#include +#include "ccnet-session-base.h" + +#include "processor.h" +#include "ccnet-db.h" + +#include "job-mgr.h" + +#include "ccnet-object.h" + +#include + +#define SESSION_CONFIG_FILENAME "ccnet.conf" +#define SESSION_PEERDB_NAME "peer-db" +#define SESSION_ID_LENGTH 40 + + +#define CCNET_TYPE_SESSION (ccnet_session_get_type ()) +#define CCNET_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_SESSION, CcnetSession)) +#define CCNET_IS_SESSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_SESSION)) +#define CCNET_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_SESSION, CcnetSessionClass)) +#define CCNET_IS_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_SESSION)) +#define CCNET_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_SESSION, CcnetSessionClass)) + + +typedef struct CcnetSession CcnetSession; +typedef struct _CcnetSessionClass CcnetSessionClass; + +struct _CcnetPeer; + +typedef struct _CcnetService { + char *svc_name; + struct _CcnetPeer *provider; +} CcnetService; + +#include + + +struct CcnetSession +{ + CcnetSessionBase base; + + struct _CcnetPeer *myself; + + /* path to the central config dir for ccnet/seafile/seahub/seafdav etc. */ + const char *central_config_dir; + /* path to ccnet specific config dir */ + const char *config_dir; + const char *config_file; + GKeyFile *keyf; + + RSA *privkey; + RSA *pubkey; + + struct _CcnetPeerManager *peer_mgr; + + struct CcnetConnManager *connMgr; + + struct _CcnetMessageManager *msg_mgr; + + struct _CcnetProcFactory *proc_factory; + + struct _CcnetPermManager *perm_mgr; + + struct _CcnetJobManager *job_mgr; + + GHashTable *service_hash; + + unsigned int saving : 1; + unsigned int saving_pub : 1; + unsigned int encrypt_channel : 1; + + int local_port; + char *un_path; + struct event local_event; + struct event local_pipe_event; + + int start_failure; /* how many times failed + to start the network */ + + sqlite3 *config_db; + + CcnetDB *db; +}; + +struct _CcnetSessionClass +{ + CcnetSessionBaseClass parent_class; + + int (*prepare) (CcnetSession *session); + void (*start) (CcnetSession *session); + + void (*on_peer_auth_done) (CcnetSession *session, struct _CcnetPeer *peer); +}; + +GType ccnet_session_get_type (); + +CcnetSession *ccnet_session_new (); + +void ccnet_session_start (CcnetSession *session); +void ccnet_session_on_exit (CcnetSession *session); +void ccnet_session_save (CcnetSession *session); + +int ccnet_session_prepare (CcnetSession *session, + const char *central_config_dir_r, + const char *config_dir_r, + gboolean test_config); + +void ccnet_session_save_config (CcnetSession *session); + +void ccnet_session_start_network (CcnetSession *session); +void ccnet_session_shutdown_network (CcnetSession *session); + +int ccnet_session_register_service (CcnetSession *session, + const char *svc_name, + const char *group, + struct _CcnetPeer *peer); + +CcnetService* ccnet_session_get_service (CcnetSession *session, + const char *service); + +void ccnet_session_unregister_service (CcnetSession *session, + struct _CcnetPeer *peer); + +gboolean ccnet_session_should_encrypt_channel (CcnetSession *session); + +#endif diff --git a/net/server/Makefile.am b/net/server/Makefile.am new file mode 100644 index 0000000..40d076f --- /dev/null +++ b/net/server/Makefile.am @@ -0,0 +1,90 @@ + +AM_CPPFLAGS = @GLIB2_CFLAGS@ @GOBJECT_CFLAGS@ \ + -DCCNET_SERVER \ + -I$(top_srcdir)/net/common \ + -I$(top_srcdir)/include -I$(top_srcdir)/include/ccnet \ + -I$(top_srcdir)/lib \ + -I$(top_builddir)/include \ + -I$(top_builddir)/lib \ + @SEARPC_CFLAGS@ \ + -Wall + +bin_PROGRAMS = ccnet-server + +PROC_HEADER_FILES = \ + $(addprefix ../common/processors/, \ + rcvmsg-proc.h \ + rcvcmd-proc.h \ + sendmsg-proc.h \ + getpubinfo-proc.h putpubinfo-proc.h \ + keepalive2-proc.h \ + mqserver-proc.h \ + service-proxy-proc.h service-stub-proc.h \ + rpcserver-proc.h \ + threaded-rpcserver-proc.h \ + echo-proc.h \ + sendsessionkey-proc.h \ + recvsessionkey-proc.h \ + sendsessionkey-v2-proc.h \ + recvsessionkey-v2-proc.h ) + +common_headers = + ../common/algorithms.h \ + ../common/proc-factory.h ../common/session.h \ + ../common/common.h ../common/handshake.h ../common/perm-mgr.h \ + ../common/peer.h ../common/connect-mgr.h \ + ../common/packet-io.h ../common/ccnet-config.h \ + ../common/log.h ../common/peer-mgr.h \ + ../common/message.h \ + ../common/getgateway.h ../common/message-manager.h \ + ../common/processor.h \ + ../common/peermgr-message.h \ + ../common/rpc-service.h \ + ../common/ccnet-db.h + + +noinst_HEADERS = $(common_headers) \ + server-session.h user-mgr.h group-mgr.h org-mgr.h \ + $(PROC_HEADER_FILES) + + +common_srcs = ../common/ccnet-db.c \ + ../common/session.c ../common/peer-mgr.c ../common/packet-io.c \ + ../common/message.c ../common/perm-mgr.c \ + ../common/log.c ../common/peer.c ../common/algorithms.c \ + ../common/handshake.c ../common/processor.c \ + ../common/getgateway.c ../common/connect-mgr.c \ + ../common/message-manager.c \ + ../common/proc-factory.c \ + ../common/ccnet-config.c \ + ../common/rpc-service.c \ + ../common/peermgr-message.c \ + ../common/processors/sendmsg-proc.c ../common/processors/rcvmsg-proc.c \ + ../common/processors/rcvcmd-proc.c \ + ../common/processors/putpubinfo-proc.c \ + ../common/processors/getpubinfo-proc.c \ + ../common/processors/keepalive2-proc.c \ + ../common/processors/mqserver-proc.c \ + ../common/processors/service-proxy-proc.c \ + ../common/processors/service-stub-proc.c \ + ../common/processors/rpcserver-proc.c \ + ../common/processors/threaded-rpcserver-proc.c \ + ../common/processors/echo-proc.c \ + ../common/processors/sendsessionkey-proc.c \ + ../common/processors/recvsessionkey-proc.c \ + ../common/processors/sendsessionkey-v2-proc.c \ + ../common/processors/recvsessionkey-v2-proc.c + +ccnet_server_SOURCES = ccnet-server.c \ + server-session.c user-mgr.c group-mgr.c org-mgr.c \ + $(common_srcs) + +ccnet_server_LDADD = @LIBEVENT_LIBS@ $(top_builddir)/lib/libccnetd.la \ + $(top_builddir)/net/common/db-wrapper/libdbwrapper.la \ + @GLIB2_LIBS@ @GOBJECT_LIBS@ @SSL_LIBS@ @LIB_RT@ @LIB_UUID@ -lsqlite3 \ + -lpthread \ + @LIB_WS32@ @LIB_INTL@ @LIB_IPHLPAPI@ @SEARPC_LIBS@ \ + @LDAP_LIBS@ @MYSQL_LIBS@ @PGSQL_LIBS@ + + +ccnet_server_LDFLAGS = @CONSOLE@ -no-undefined diff --git a/net/server/ccnet-server.c b/net/server/ccnet-server.c new file mode 100644 index 0000000..4f82608 --- /dev/null +++ b/net/server/ccnet-server.c @@ -0,0 +1,342 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include +#include +#include + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#include +#else +#include +#endif + +#include "server-session.h" +#include "user-mgr.h" +#include "rpc-service.h" +#include "log.h" + +char *pidfile = NULL; +CcnetSession *session; + + +#ifndef WIN32 +struct event sigint; +struct event sigterm; +struct event sigusr1; + +static void sigintHandler (int fd, short event, void *user_data) +{ + ccnet_session_on_exit (session); + exit (1); +} + +static void sigusr1Handler (int fd, short event, void *user_data) +{ + ccnet_log_reopen (); +} + +static void setSigHandlers () +{ + signal (SIGPIPE, SIG_IGN); + + event_set(&sigint, SIGINT, EV_SIGNAL, sigintHandler, NULL); + event_add(&sigint, NULL); + + /* same as sigint */ + event_set(&sigterm, SIGTERM, EV_SIGNAL, sigintHandler, NULL); + event_add(&sigterm, NULL); + + /* redesign as reopen log */ + event_set(&sigusr1, SIGUSR1, EV_SIGNAL | EV_PERSIST, sigusr1Handler, NULL); + event_add(&sigusr1, NULL); +} +#endif + +static void +remove_pidfile (const char *pidfile) +{ + if (pidfile) { + g_unlink (pidfile); + } +} + +static int +write_pidfile (const char *pidfile_path) +{ + if (!pidfile_path) + return -1; + + pid_t pid = getpid(); + + FILE *pidfile = fopen(pidfile_path, "w"); + if (!pidfile) { + ccnet_warning ("Failed to fopen() pidfile %s: %s\n", + pidfile_path, strerror(errno)); + return -1; + } + + char buf[32]; + snprintf (buf, sizeof(buf), "%d\n", pid); + if (fputs(buf, pidfile) < 0) { + ccnet_warning ("Failed to write pidfile %s: %s\n", + pidfile_path, strerror(errno)); + return -1; + } + + fflush (pidfile); + fclose (pidfile); + return 0; +} + +static void +on_ccnet_exit(void) +{ + if (pidfile) + remove_pidfile (pidfile); +} + + +static const char *short_options = "hvdtc:D:f:P:M:F:"; +static struct option long_options[] = { + { "help", no_argument, NULL, 'h', }, + { "version", no_argument, NULL, 'v', }, + { "config-dir", required_argument, NULL, 'c' }, + { "central-config-dir", required_argument, NULL, 'F' }, + { "logfile", required_argument, NULL, 'f' }, + { "debug", required_argument, NULL, 'D' }, + { "daemon", no_argument, NULL, 'd' }, + { "pidfile", required_argument, NULL, 'P' }, + { "max-users", required_argument, NULL, 'M' }, + { "test-config", required_argument, NULL, 't' }, + { NULL, 0, NULL, 0, }, +}; + + +static void usage() +{ + fputs( +"usage: ccnet-server [OPTIONS]\n\n" +"Supported OPTIONS are:\n" +" -c CONFDIR\n" +" Specify the ccnet configuration directory. Default is ~/.ccnet\n" +" -d\n" +" Run ccnet as a daemon\n" +" -D FLAGS\n" +" Specify debug flags for logging, for example\n" +" Peer,Processor\n" +" supported flags are\n" +" Peer,Processor,Netio,\n" +" Message,Connection,Other\n" +" or ALL to enable all debug flags\n" +" -f LOG_FILE\n" +" Log file path\n" +" -P PIDFILE\n" +" Specify the file to store pid\n" +" -M MAX_USERS\n" +" Specify the max users for login\n" +" -t\n" +" test ccnet configuration and exit\n", + stdout); +} + +int test_ccnet_config(const char *central_config_dir, const char *config_dir, int max_users) +{ +#if !GLIB_CHECK_VERSION(2, 36, 0) + g_type_init (); +#endif + + config_dir = ccnet_expand_path (config_dir); + if (central_config_dir) { + central_config_dir = ccnet_expand_path (central_config_dir); + } + + if (ccnet_log_init ("-", "debug") < 0) { + fprintf (stderr, "ccnet_log_init error: %s\n", strerror(errno)); + return -1; + } + + srand (time(NULL)); + + session = (CcnetSession *)ccnet_server_session_new (); + if (!session) { + fprintf (stderr, "Error: failed to create ccnet session\n"); + return -1; + } + + event_init (); + evdns_init (); + ccnet_user_manager_set_max_users (((struct CcnetServerSession *)session)->user_mgr, max_users); + if (ccnet_session_prepare(session, central_config_dir, config_dir, TRUE) < 0) { + return -1; + } + + return 0; +} + +int +main (int argc, char **argv) +{ + int c; + char *config_dir; + char *central_config_dir = NULL; + char *log_file = 0; + const char *debug_str = 0; + int daemon_mode = 0; + int max_users = 0; + const char *log_level_str = "debug"; + gboolean test_config = FALSE; + + config_dir = DEFAULT_CONFIG_DIR; + +#ifdef WIN32 + argv = get_argv_utf8 (&argc); +#endif + while ((c = getopt_long (argc, argv, short_options, + long_options, NULL)) != EOF) { + switch (c) { + case 'h': + usage(); + exit(0); + break; + case 'v': + exit (1); + break; + case 'c': + config_dir = optarg; + break; + case 'F': + central_config_dir = optarg; + break; + case 'f': + log_file = optarg; + break; + case 'D': + debug_str = optarg; + break; + case 'd': + daemon_mode = 1; + break; + case 'P': + pidfile = optarg; + break; + case 'M': + max_users = atoi(optarg); + break; + case 't': + test_config = TRUE; + break; + default: + fprintf (stderr, "unknown option \"-%c\"\n", (char)c); + usage(); + exit (1); + } + } + + + argc -= optind; + argv += optind; + + if (config_dir == NULL) { + fprintf (stderr, "Missing config dir\n"); + exit (1); + } + + if (test_config) { + /* test ccnet configuration and exit */ + return test_ccnet_config (central_config_dir, config_dir, max_users); + } + +#ifndef WIN32 + if (daemon_mode) { +#ifndef __APPLE__ + daemon (1, 0); +#else /* __APPLE */ + /* daemon is deprecated under APPLE + * use fork() instead + * */ + switch (fork ()) { + case -1: + ccnet_warning ("Failed to daemonize"); + exit (-1); + break; + case 0: + /* all good*/ + break; + default: + /* kill origin process */ + exit (0); + } +#endif /* __APPLE */ + } +#else /* WIN32 */ + WSADATA wsadata; + WSAStartup(0x0101, &wsadata); +#endif + +#if !GLIB_CHECK_VERSION(2, 36, 0) + g_type_init (); +#endif + + /* log */ + if (!debug_str) + debug_str = g_getenv("CCNET_DEBUG"); + ccnet_debug_set_flags_string (debug_str); + + config_dir = ccnet_expand_path (config_dir); + + if (!log_file) { + char *logdir = g_build_filename (config_dir, "logs", NULL); + checkdir_with_mkdir (logdir); + g_free (logdir); + log_file = g_build_filename (config_dir, "logs", "ccnet.log", NULL); + } + if (ccnet_log_init (log_file, log_level_str) < 0) { + fprintf (stderr, "ccnet_log_init error: %s, %s\n", strerror(errno), + log_file); + exit (1); + } + + srand (time(NULL)); + + session = (CcnetSession *)ccnet_server_session_new (); + if (!session) { + fputs ("Error: failed to start ccnet session, " + "see log file for the detail.\n", stderr); + return -1; + } + + event_init (); + evdns_init (); + ccnet_user_manager_set_max_users (((struct CcnetServerSession *)session)->user_mgr, max_users); + if (ccnet_session_prepare(session, central_config_dir, config_dir, FALSE) < 0) { + fputs ("Error: failed to start ccnet session, " + "see log file for the detail.\n", stderr); + return -1; + } + + /* write pidfile after session_prepare success, if there is + * another instance of ccnet session_prepare will failed. + */ + if (pidfile) { + if (write_pidfile (pidfile) < 0) { + ccnet_message ("Failed to write pidfile\n"); + return -1; + } + } + atexit (on_ccnet_exit); + +#ifndef WIN32 + setSigHandlers(); +#endif + + ccnet_session_start (session); + ccnet_start_rpc(session); + + /* actually enter the event loop */ + event_dispatch (); + + return 0; +} + diff --git a/net/server/group-mgr.c b/net/server/group-mgr.c new file mode 100644 index 0000000..0c95cf0 --- /dev/null +++ b/net/server/group-mgr.c @@ -0,0 +1,785 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "server-session.h" + +#include "ccnet-db.h" +#include "group-mgr.h" +#include "org-mgr.h" + +#include "utils.h" +#include "log.h" + +struct _CcnetGroupManagerPriv { + CcnetDB *db; +}; + +static int open_db (CcnetGroupManager *manager); +static int check_db_table (CcnetDB *db); + +CcnetGroupManager* ccnet_group_manager_new (CcnetSession *session) +{ + CcnetGroupManager *manager = g_new0 (CcnetGroupManager, 1); + + manager->session = session; + manager->priv = g_new0 (CcnetGroupManagerPriv, 1); + + return manager; +} + +int +ccnet_group_manager_prepare (CcnetGroupManager *manager) +{ + return open_db(manager); +} + +void ccnet_group_manager_start (CcnetGroupManager *manager) +{ +} + +static CcnetDB * +open_sqlite_db (CcnetGroupManager *manager) +{ + CcnetDB *db = NULL; + char *db_dir; + char *db_path; + + db_dir = g_build_filename (manager->session->config_dir, "GroupMgr", NULL); + if (checkdir_with_mkdir(db_dir) < 0) { + ccnet_error ("Cannot open db dir %s: %s\n", db_dir, + strerror(errno)); + g_free (db_dir); + return NULL; + } + g_free (db_dir); + + db_path = g_build_filename (manager->session->config_dir, "GroupMgr", + "groupmgr.db", NULL); + db = ccnet_db_new_sqlite (db_path); + + g_free (db_path); + + return db; +} + +static int +open_db (CcnetGroupManager *manager) +{ + CcnetDB *db = NULL; + + switch (ccnet_db_type(manager->session->db)) { + case CCNET_DB_TYPE_SQLITE: + db = open_sqlite_db (manager); + break; + case CCNET_DB_TYPE_PGSQL: + case CCNET_DB_TYPE_MYSQL: + db = manager->session->db; + break; + } + + if (!db) + return -1; + + manager->priv->db = db; + return check_db_table (db); +} + +/* -------- Group Database Management ---------------- */ + +static int check_db_table (CcnetDB *db) +{ + char *sql; + + int db_type = ccnet_db_type (db); + if (db_type == CCNET_DB_TYPE_MYSQL) { + sql = "CREATE TABLE IF NOT EXISTS `Group` (`group_id` INTEGER" + " PRIMARY KEY AUTO_INCREMENT, `group_name` VARCHAR(255)," + " `creator_name` VARCHAR(255), `timestamp` BIGINT," + " `type` VARCHAR(32))" + "ENGINE=INNODB"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS `GroupUser` (`group_id` INTEGER," + " `user_name` VARCHAR(255), `is_staff` tinyint, PRIMARY KEY" + " (`group_id`, `user_name`), INDEX (`user_name`))" + "ENGINE=INNODB"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS GroupDNPair (group_id INTEGER," + " dn VARCHAR(255))ENGINE=INNODB"; + if (ccnet_db_query (db, sql) < 0) + return -1; + } else if (db_type == CCNET_DB_TYPE_SQLITE) { + sql = "CREATE TABLE IF NOT EXISTS `Group` (`group_id` INTEGER" + " PRIMARY KEY AUTOINCREMENT, `group_name` VARCHAR(255)," + " `creator_name` VARCHAR(255), `timestamp` BIGINT," + " `type` VARCHAR(32))"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS `GroupUser` (`group_id` INTEGER, " + "`user_name` VARCHAR(255), `is_staff` tinyint)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE UNIQUE INDEX IF NOT EXISTS groupid_username_indx on " + "`GroupUser` (`group_id`, `user_name`)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE INDEX IF NOT EXISTS username_indx on " + "`GroupUser` (`user_name`)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS GroupDNPair (group_id INTEGER," + " dn VARCHAR(255))"; + if (ccnet_db_query (db, sql) < 0) + return -1; + } else if (db_type == CCNET_DB_TYPE_PGSQL) { + sql = "CREATE TABLE IF NOT EXISTS \"Group\" (group_id SERIAL" + " PRIMARY KEY, group_name VARCHAR(255)," + " creator_name VARCHAR(255), timestamp BIGINT," + " type VARCHAR(32))"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS GroupUser (group_id INTEGER," + " user_name VARCHAR(255), is_staff smallint, UNIQUE " + " (group_id, user_name))"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + if (!pgsql_index_exists (db, "groupuser_username_idx")) { + sql = "CREATE INDEX groupuser_username_idx ON GroupUser (user_name)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + } + + sql = "CREATE TABLE IF NOT EXISTS GroupDNPair (group_id INTEGER," + " dn VARCHAR(255))"; + if (ccnet_db_query (db, sql) < 0) + return -1; + } + + return 0; +} + +static int +create_group_common (CcnetGroupManager *mgr, + const char *group_name, + const char *user_name, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + gint64 now = get_current_time(); + char *sql; + int group_id = -1; + + char *user_name_l = g_ascii_strdown (user_name, -1); + + if (ccnet_db_type(db) == CCNET_DB_TYPE_PGSQL) + sql = "INSERT INTO \"Group\"(group_name, " + "creator_name, timestamp) VALUES(?, ?, ?)"; + else + sql = "INSERT INTO `Group`(group_name, " + "creator_name, timestamp) VALUES(?, ?, ?)"; + + if (ccnet_db_statement_query (db, sql, 3, + "string", group_name, "string", user_name_l, + "int64", now) < 0) { + g_set_error (error, CCNET_DOMAIN, 0, "Failed to create group"); + goto out; + } + + if (ccnet_db_type(db) == CCNET_DB_TYPE_PGSQL) + sql = "SELECT group_id FROM \"Group\" WHERE " + "group_name = ? AND creator_name = ? " + "AND timestamp = ?"; + else + sql = "SELECT group_id FROM `Group` WHERE " + "group_name = ? AND creator_name = ? " + "AND timestamp = ?"; + + group_id = ccnet_db_statement_get_int (db, sql, 3, + "string", group_name, "string", user_name_l, + "int64", now); + if (group_id < 0) { + g_set_error (error, CCNET_DOMAIN, 0, "Failed to create group"); + goto out; + } + + sql = "INSERT INTO GroupUser VALUES (?, ?, ?)"; + + if (ccnet_db_statement_query (db, sql, 3, + "int", group_id, "string", user_name_l, + "int", 1) < 0) { + if (ccnet_db_type(db) == CCNET_DB_TYPE_PGSQL) + sql = "DELETE FROM \"Group\" WHERE group_id=?"; + else + sql = "DELETE FROM `Group` WHERE group_id=?"; + ccnet_db_statement_query (db, sql, 1, "int", group_id); + g_set_error (error, CCNET_DOMAIN, 0, "Failed to create group"); + group_id = -1; + goto out; + } + +out: + g_free (user_name_l); + return group_id; +} + +/* static gboolean */ +/* duplicate_group_name (CcnetGroupManager *mgr, */ +/* const char *group_name, */ +/* const char *user_name) */ +/* { */ +/* GList *groups = NULL, *ptr; */ +/* CcnetOrgManager *org_mgr = NULL; */ +/* gboolean ret = FALSE; */ + +/* groups = ccnet_group_manager_get_all_groups (mgr, -1, -1, NULL); */ +/* if (!groups) { */ +/* return FALSE; */ +/* } */ + +/* for (ptr = groups; ptr; ptr = ptr->next) { */ +/* CcnetGroup *group = (CcnetGroup *)ptr->data; */ +/* org_mgr = ((CcnetServerSession *)(mgr->session))->org_mgr; */ +/* if (ccnet_org_manager_is_org_group(org_mgr, ccnet_group_get_id (group), */ +/* NULL)) { */ +/* /\* Skip org groups. *\/ */ +/* continue; */ +/* } */ + +/* if (g_strcmp0 (group_name, ccnet_group_get_group_name (group)) == 0) { */ +/* ret = TRUE; */ +/* goto out; */ +/* } */ +/* } */ + +/* out: */ +/* for (ptr = groups; ptr; ptr = ptr->next) */ +/* g_object_unref ((GObject *)ptr->data); */ +/* g_list_free (groups); */ +/* return ret; */ +/* } */ + +int ccnet_group_manager_create_group (CcnetGroupManager *mgr, + const char *group_name, + const char *user_name, + GError **error) +{ + + /* if (duplicate_group_name (mgr, group_name, user_name)) { */ + /* g_set_error (error, CCNET_DOMAIN, 0, "The group has already created"); */ + /* return -1; */ + /* } */ + + return create_group_common (mgr, group_name, user_name, error); +} + +static gboolean +duplicate_org_group_name (CcnetGroupManager *mgr, + int org_id, + const char *group_name) +{ + GList *org_groups = NULL, *ptr; + CcnetOrgManager *org_mgr = ((CcnetServerSession *)(mgr->session))->org_mgr; + + org_groups = ccnet_org_manager_get_org_groups (org_mgr, org_id, -1, -1); + if (!org_groups) + return FALSE; + + for (ptr = org_groups; ptr; ptr = ptr->next) { + int group_id = (int)(long)ptr->data; + CcnetGroup *group = ccnet_group_manager_get_group (mgr, group_id, + NULL); + if (!group) + continue; + + if (g_strcmp0 (group_name, ccnet_group_get_group_name(group)) == 0) { + g_list_free (org_groups); + g_object_unref (group); + return TRUE; + } else { + g_object_unref (group); + } + } + + g_list_free (org_groups); + return FALSE; +} + +int ccnet_group_manager_create_org_group (CcnetGroupManager *mgr, + int org_id, + const char *group_name, + const char *user_name, + GError **error) +{ + CcnetOrgManager *org_mgr = ((CcnetServerSession *)(mgr->session))->org_mgr; + + if (duplicate_org_group_name (mgr, org_id, group_name)) { + g_set_error (error, CCNET_DOMAIN, 0, + "The group has already created in this org."); + return -1; + } + + int group_id = create_group_common (mgr, group_name, user_name, error); + if (group_id < 0) { + g_set_error (error, CCNET_DOMAIN, 0, "Failed to create org group."); + return -1; + } + + if (ccnet_org_manager_add_org_group (org_mgr, org_id, group_id, + error) < 0) { + g_set_error (error, CCNET_DOMAIN, 0, "Failed to create org group."); + return -1; + } + + return group_id; +} + +static gboolean +check_group_staff (CcnetDB *db, int group_id, const char *user_name) +{ + return ccnet_db_statement_exists (db, "SELECT group_id FROM GroupUser WHERE " + "group_id = ? AND user_name = ? AND " + "is_staff = 1", + 2, "int", group_id, "string", user_name); +} + +int ccnet_group_manager_remove_group (CcnetGroupManager *mgr, + int group_id, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + char *sql; + + /* No permission check here, since both group staff and seahub staff + * can remove group. + */ + + if (ccnet_db_type(db) == CCNET_DB_TYPE_PGSQL) + sql = "DELETE FROM \"Group\" WHERE group_id=?"; + else + sql = "DELETE FROM `Group` WHERE group_id=?"; + ccnet_db_statement_query (db, sql, 1, "int", group_id); + + sql = "DELETE FROM GroupUser WHERE group_id=?"; + ccnet_db_statement_query (db, sql, 1, "int", group_id); + + return 0; +} + +static gboolean +check_group_exists (CcnetDB *db, int group_id) +{ + if (ccnet_db_type(db) == CCNET_DB_TYPE_PGSQL) + return ccnet_db_statement_exists (db, "SELECT group_id FROM \"Group\" WHERE " + "group_id=?", 1, "int", group_id); + else + return ccnet_db_statement_exists (db, "SELECT group_id FROM `Group` WHERE " + "group_id=?", 1, "int", group_id); +} + +int ccnet_group_manager_add_member (CcnetGroupManager *mgr, + int group_id, + const char *user_name, + const char *member_name, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + + /* check whether user is the staff of the group */ + if (!check_group_staff (db, group_id, user_name)) { + g_set_error (error, CCNET_DOMAIN, 0, + "Permission error: only group staff can add member"); + return -1; + } + + /* check whether group exists */ + if (!check_group_exists (db, group_id)) { + g_set_error (error, CCNET_DOMAIN, 0, "Group not exists"); + return -1; + } + + /* check whether group is full */ + /* snprintf (sql, sizeof(sql), "SELECT count(group_id) FROM `GroupUser` " */ + /* "WHERE `group_id` = %d", group_id); */ + /* int count = ccnet_db_get_int (db, sql); */ + /* if (count >= MAX_GROUP_MEMBERS) { */ + /* g_set_error (error, CCNET_DOMAIN, 0, "Group is full"); */ + /* return -1; */ + /* } */ + + char *member_name_l = g_ascii_strdown (member_name, -1); + int rc = ccnet_db_statement_query (db, "INSERT INTO GroupUser VALUES (?, ?, ?)", + 3, "int", group_id, "string", member_name_l, + "int", 0); + g_free (member_name_l); + if (rc < 0) { + g_set_error (error, CCNET_DOMAIN, 0, "Failed to add member to group"); + return -1; + } + + return 0; +} + +int ccnet_group_manager_remove_member (CcnetGroupManager *mgr, + int group_id, + const char *user_name, + const char *member_name, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + char *sql; + + /* check whether user is the staff of the group */ + if (!check_group_staff (db, group_id, user_name)) { + g_set_error (error, CCNET_DOMAIN, 0, + "Only group staff can remove member"); + return -1; + } + + /* check whether group exists */ + if (!check_group_exists (db, group_id)) { + g_set_error (error, CCNET_DOMAIN, 0, "Group not exists"); + return -1; + } + + /* can not remove myself */ + if (g_strcmp0 (user_name, member_name) == 0) { + g_set_error (error, CCNET_DOMAIN, 0, "Can not remove myself"); + return -1; + } + + sql = "DELETE FROM GroupUser WHERE group_id=? AND user_name=?"; + ccnet_db_statement_query (db, sql, 2, "int", group_id, "string", member_name); + + return 0; +} + +int ccnet_group_manager_set_admin (CcnetGroupManager *mgr, + int group_id, + const char *member_name, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + + ccnet_db_statement_query (db, + "UPDATE GroupUser SET is_staff = 1 " + "WHERE group_id = ? and user_name = ?", + 2, "int", group_id, "string", member_name); + + return 0; +} + +int ccnet_group_manager_unset_admin (CcnetGroupManager *mgr, + int group_id, + const char *member_name, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + + ccnet_db_statement_query (db, + "UPDATE GroupUser SET is_staff = 0 " + "WHERE group_id = ? and user_name = ?", + 2, "int", group_id, "string", member_name); + + return 0; +} + +int ccnet_group_manager_set_group_name (CcnetGroupManager *mgr, + int group_id, + const char *group_name, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + + ccnet_db_statement_query (db, + "UPDATE `Group` SET group_name = ? " + "WHERE group_id = ?", + 2, "string", group_name, "int", group_id); + + return 0; +} + +int ccnet_group_manager_quit_group (CcnetGroupManager *mgr, + int group_id, + const char *user_name, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + + /* check where user is the staff of the group */ + if (check_group_staff (db, group_id, user_name)) { + g_set_error (error, CCNET_DOMAIN, 0, + "Group staff can not quit group"); + return -1; + } + + /* check whether group exists */ + if (!check_group_exists (db, group_id)) { + g_set_error (error, CCNET_DOMAIN, 0, "Group not exists"); + return -1; + } + + ccnet_db_statement_query (db, + "DELETE FROM GroupUser WHERE group_id=? " + "AND user_name=?", + 2, "int", group_id, "string", user_name); + + return 0; +} + +static gboolean +get_group_ids_cb (CcnetDBRow *row, void *data) +{ + GList **plist = data; + + int group_id = ccnet_db_row_get_column_int (row, 0); + + *plist = g_list_prepend (*plist, (gpointer)(long)group_id); + + return TRUE; +} + +GList * +ccnet_group_manager_get_groupids_by_user (CcnetGroupManager *mgr, + const char *user_name, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + GList *group_ids = NULL; + + if (ccnet_db_statement_foreach_row (db, + "SELECT group_id FROM GroupUser " + "WHERE user_name=?", + get_group_ids_cb, &group_ids, + 1, "string", user_name) < 0) { + g_list_free (group_ids); + return NULL; + } + + return g_list_reverse (group_ids); +} + +static gboolean +get_ccnetgroup_cb (CcnetDBRow *row, void *data) +{ + CcnetGroup **p_group = data; + int group_id; + const char *group_name; + const char *creator; + gint64 ts; + + group_id = ccnet_db_row_get_column_int (row, 0); + group_name = (const char *)ccnet_db_row_get_column_text (row, 1); + creator = (const char *)ccnet_db_row_get_column_text (row, 2); + ts = ccnet_db_row_get_column_int64 (row, 3); + + char *creator_l = g_ascii_strdown (creator, -1); + *p_group = g_object_new (CCNET_TYPE_GROUP, + "id", group_id, + "group_name", group_name, + "creator_name", creator_l, + "timestamp", ts, + "source", "DB", + NULL); + g_free (creator_l); + + return FALSE; +} + +CcnetGroup * +ccnet_group_manager_get_group (CcnetGroupManager *mgr, int group_id, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + char *sql; + CcnetGroup *ccnetgroup = NULL; + + if (ccnet_db_type(db) == CCNET_DB_TYPE_PGSQL) + sql = "SELECT * FROM \"Group\" WHERE group_id = ?"; + else + sql = "SELECT * FROM `Group` WHERE group_id = ?"; + if (ccnet_db_statement_foreach_row (db, sql, + get_ccnetgroup_cb, &ccnetgroup, + 1, "int", group_id) < 0) + return NULL; + + return ccnetgroup; +} + +static gboolean +get_ccnet_groupuser_cb (CcnetDBRow *row, void *data) +{ + GList **plist = data; + CcnetGroupUser *group_user; + + int group_id = ccnet_db_row_get_column_int (row, 0); + const char *user = (const char *)ccnet_db_row_get_column_text (row, 1); + int is_staff = ccnet_db_row_get_column_int (row, 2); + + char *user_l = g_ascii_strdown (user, -1); + group_user = g_object_new (CCNET_TYPE_GROUP_USER, + "group_id", group_id, + "user_name", user_l, + "is_staff", is_staff, + NULL); + g_free (user_l); + if (group_user != NULL) { + *plist = g_list_prepend (*plist, group_user); + } + + return TRUE; +} + +GList * +ccnet_group_manager_get_group_members (CcnetGroupManager *mgr, int group_id, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + char *sql; + GList *group_users = NULL; + + sql = "SELECT * FROM GroupUser WHERE group_id = ?"; + if (ccnet_db_statement_foreach_row (db, sql, + get_ccnet_groupuser_cb, &group_users, + 1, "int", group_id) < 0) + return NULL; + + return g_list_reverse (group_users); +} + +int +ccnet_group_manager_check_group_staff (CcnetGroupManager *mgr, + int group_id, + const char *user_name) +{ + return check_group_staff (mgr->priv->db, group_id, user_name); +} + +int +ccnet_group_manager_remove_group_user (CcnetGroupManager *mgr, + const char *user) +{ + CcnetDB *db = mgr->priv->db; + + ccnet_db_statement_query (db, + "DELETE FROM GroupUser " + "WHERE user_name = ?", + 1, "string", user); + + return 0; +} + +int +ccnet_group_manager_is_group_user (CcnetGroupManager *mgr, + int group_id, + const char *user) +{ + CcnetDB *db = mgr->priv->db; + + return ccnet_db_statement_exists (db, "SELECT group_id FROM GroupUser " + "WHERE group_id=? AND user_name=?", + 2, "int", group_id, "string", user); +} + +static gboolean +get_all_ccnetgroups_cb (CcnetDBRow *row, void *data) +{ + GList **plist = data; + int group_id; + const char *group_name; + const char *creator; + gint64 ts; + + group_id = ccnet_db_row_get_column_int (row, 0); + group_name = (const char *)ccnet_db_row_get_column_text (row, 1); + creator = (const char *)ccnet_db_row_get_column_text (row, 2); + ts = ccnet_db_row_get_column_int64 (row, 3); + + char *creator_l = g_ascii_strdown (creator, -1); + CcnetGroup *group = g_object_new (CCNET_TYPE_GROUP, + "id", group_id, + "group_name", group_name, + "creator_name", creator_l, + "timestamp", ts, + "source", "DB", + NULL); + g_free (creator_l); + + *plist = g_list_prepend (*plist, group); + + return TRUE; +} + +GList* +ccnet_group_manager_get_all_groups (CcnetGroupManager *mgr, + int start, int limit, GError **error) +{ + CcnetDB *db = mgr->priv->db; + GList *ret = NULL; + int rc; + + if (ccnet_db_type(mgr->priv->db) == CCNET_DB_TYPE_PGSQL) { + if (start == -1 && limit == -1) { + rc = ccnet_db_statement_foreach_row (db, "SELECT group_id, group_name, " + "creator_name, timestamp FROM \"Group\" " + "ORDER BY timestamp DESC", + get_all_ccnetgroups_cb, &ret, + 0); + } else { + rc = ccnet_db_statement_foreach_row (db, "SELECT group_id, group_name, " + "creator_name, timestamp FROM \"Group\" " + "ORDER BY timestamp DESC LIMIT ? OFFSET ?", + get_all_ccnetgroups_cb, &ret, + 2, "int", limit, "int", start); + } + } else { + if (start == -1 && limit == -1) { + rc = ccnet_db_statement_foreach_row (db, "SELECT `group_id`, `group_name`, " + "`creator_name`, `timestamp` FROM `Group` " + "ORDER BY timestamp DESC", + get_all_ccnetgroups_cb, &ret, + 0); + } else { + rc = ccnet_db_statement_foreach_row (db, "SELECT `group_id`, `group_name`, " + "`creator_name`, `timestamp` FROM `Group` " + "ORDER BY timestamp DESC LIMIT ?, ?", + get_all_ccnetgroups_cb, &ret, + 2, "int", start, "int", limit); + } + } + + if (rc < 0) + return NULL; + + return g_list_reverse (ret); +} + +int +ccnet_group_manager_set_group_creator (CcnetGroupManager *mgr, + int group_id, + const char *user_name) +{ + CcnetDB *db = mgr->priv->db; + char *sql; + + if (ccnet_db_type(db) == CCNET_DB_TYPE_PGSQL) { + sql = "UPDATE \"Group\" SET creator_name = ? WHERE group_id = ?"; + } else { + sql = "UPDATE `Group` SET creator_name = ? WHERE group_id = ?"; + } + + ccnet_db_statement_query (db, sql, 2, "string", user_name, "int", group_id); + + return 0; + +} + diff --git a/net/server/group-mgr.h b/net/server/group-mgr.h new file mode 100644 index 0000000..5500b3e --- /dev/null +++ b/net/server/group-mgr.h @@ -0,0 +1,111 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef GROUP_MGR_H +#define GROUP_MGR_H + +#include "../common/session.h" + +/* #define MAX_GROUP_MEMBERS 16 */ + +typedef struct _CcnetGroupManager CcnetGroupManager; +typedef struct _CcnetGroupManagerPriv CcnetGroupManagerPriv; + +struct _CcnetGroupManager +{ + CcnetSession *session; + + CcnetGroupManagerPriv *priv; +}; + +CcnetGroupManager* ccnet_group_manager_new (CcnetSession *session); + +int +ccnet_group_manager_prepare (CcnetGroupManager *manager); + +void ccnet_group_manager_start (CcnetGroupManager *manager); + +int ccnet_group_manager_create_group (CcnetGroupManager *mgr, + const char *group_name, + const char *user_name, + GError **error); + +int ccnet_group_manager_create_org_group (CcnetGroupManager *mgr, + int org_id, + const char *group_name, + const char *user_name, + GError **error); + +int ccnet_group_manager_remove_group (CcnetGroupManager *mgr, + int group_id, + GError **error); + +int ccnet_group_manager_add_member (CcnetGroupManager *mgr, + int group_id, + const char *user_name, + const char *member_name, + GError **error); + +int ccnet_group_manager_remove_member (CcnetGroupManager *mgr, + int group_id, + const char *user_name, + const char *member_name, + GError **error); + +int ccnet_group_manager_set_admin (CcnetGroupManager *mgr, + int group_id, + const char *member_name, + GError **error); + +int ccnet_group_manager_unset_admin (CcnetGroupManager *mgr, + int group_id, + const char *member_name, + GError **error); + +int ccnet_group_manager_set_group_name (CcnetGroupManager *mgr, + int group_id, + const char *group_name, + GError **error); + +int ccnet_group_manager_quit_group (CcnetGroupManager *mgr, + int group_id, + const char *user_name, + GError **error); + +GList * +ccnet_group_manager_get_groupids_by_user (CcnetGroupManager *mgr, + const char *user_name, + GError **error); + +CcnetGroup * +ccnet_group_manager_get_group (CcnetGroupManager *mgr, int group_id, + GError **error); + +GList * +ccnet_group_manager_get_group_members (CcnetGroupManager *mgr, int group_id, + GError **error); + +int +ccnet_group_manager_check_group_staff (CcnetGroupManager *mgr, + int group_id, + const char *user_name); + +int +ccnet_group_manager_remove_group_user (CcnetGroupManager *mgr, + const char *user); + +int +ccnet_group_manager_is_group_user (CcnetGroupManager *mgr, + int group_id, + const char *user); + +GList* +ccnet_group_manager_get_all_groups (CcnetGroupManager *mgr, + int start, int limit, GError **error); + +int +ccnet_group_manager_set_group_creator (CcnetGroupManager *mgr, + int group_id, + const char *user_name); + +#endif /* GROUP_MGR_H */ + diff --git a/net/server/org-mgr.c b/net/server/org-mgr.c new file mode 100644 index 0000000..8ce7c0d --- /dev/null +++ b/net/server/org-mgr.c @@ -0,0 +1,686 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "ccnet-db.h" +#include "org-mgr.h" + +#include "log.h" + +struct _CcnetOrgManagerPriv +{ + CcnetDB *db; +}; + +static int open_db (CcnetOrgManager *manager); +static int check_db_table (CcnetDB *db); + +CcnetOrgManager* ccnet_org_manager_new (CcnetSession *session) +{ + CcnetOrgManager *manager = g_new0 (CcnetOrgManager, 1); + + manager->session = session; + manager->priv = g_new0 (CcnetOrgManagerPriv, 1); + + return manager; +} + +int +ccnet_org_manager_prepare (CcnetOrgManager *manager) +{ + return open_db (manager); +} + +static CcnetDB * +open_sqlite_db (CcnetOrgManager *manager) +{ + CcnetDB *db = NULL; + char *db_dir; + char *db_path; + + db_dir = g_build_filename (manager->session->config_dir, "OrgMgr", NULL); + if (checkdir_with_mkdir(db_dir) < 0) { + ccnet_error ("Cannot open db dir %s: %s\n", db_dir, + strerror(errno)); + g_free (db_dir); + return NULL; + } + g_free (db_dir); + + db_path = g_build_filename (manager->session->config_dir, "OrgMgr", + "orgmgr.db", NULL); + db = ccnet_db_new_sqlite (db_path); + + g_free (db_path); + + return db; +} + +static int +open_db (CcnetOrgManager *manager) +{ + CcnetDB *db = NULL; + + switch (ccnet_db_type(manager->session->db)) { + case CCNET_DB_TYPE_SQLITE: + db = open_sqlite_db (manager); + break; + case CCNET_DB_TYPE_PGSQL: + case CCNET_DB_TYPE_MYSQL: + db = manager->session->db; + break; + } + + if (!db) + return -1; + + manager->priv->db = db; + return check_db_table (db); +} + +void ccnet_org_manager_start (CcnetOrgManager *manager) +{ +} + +/* -------- Group Database Management ---------------- */ + +static int check_db_table (CcnetDB *db) +{ + char *sql; + + int db_type = ccnet_db_type (db); + if (db_type == CCNET_DB_TYPE_MYSQL) { + sql = "CREATE TABLE IF NOT EXISTS Organization (org_id INTEGER" + " PRIMARY KEY AUTO_INCREMENT, org_name VARCHAR(255)," + " url_prefix VARCHAR(255), creator VARCHAR(255), ctime BIGINT," + " UNIQUE INDEX (url_prefix))" + "ENGINE=INNODB"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS OrgUser (org_id INTEGER, " + "email VARCHAR(255), is_staff BOOL NOT NULL, " + "INDEX (email), PRIMARY KEY (org_id, email))" + "ENGINE=INNODB"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS OrgGroup (org_id INTEGER, " + "group_id INTEGER, INDEX (group_id), " + "PRIMARY KEY (org_id, group_id))" + "ENGINE=INNODB"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + } else if (db_type == CCNET_DB_TYPE_SQLITE) { + sql = "CREATE TABLE IF NOT EXISTS Organization (org_id INTEGER" + " PRIMARY KEY AUTOINCREMENT, org_name VARCHAR(255)," + " url_prefix VARCHAR(255), " + " creator VARCHAR(255), ctime BIGINT)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE UNIQUE INDEX IF NOT EXISTS url_prefix_indx on " + "Organization (url_prefix)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS OrgUser (org_id INTEGER, " + "email TEXT, is_staff bool NOT NULL)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE INDEX IF NOT EXISTS email_indx on " + "OrgUser (email)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE UNIQUE INDEX IF NOT EXISTS orgid_email_indx on " + "OrgUser (org_id, email)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS OrgGroup (org_id INTEGER, " + "group_id INTEGER)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE INDEX IF NOT EXISTS groupid_indx on OrgGroup (group_id)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE UNIQUE INDEX IF NOT EXISTS org_group_indx on " + "OrgGroup (org_id, group_id)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + } else if (db_type == CCNET_DB_TYPE_PGSQL) { + sql = "CREATE TABLE IF NOT EXISTS Organization (org_id SERIAL" + " PRIMARY KEY, org_name VARCHAR(255)," + " url_prefix VARCHAR(255), creator VARCHAR(255), ctime BIGINT," + " UNIQUE (url_prefix))"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS OrgUser (org_id INTEGER, " + "email VARCHAR(255), is_staff INTEGER NOT NULL, " + "UNIQUE (org_id, email))"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + if (!pgsql_index_exists (db, "orguser_email_idx")) { + sql = "CREATE INDEX orguser_email_idx ON OrgUser (email)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + } + + sql = "CREATE TABLE IF NOT EXISTS OrgGroup (org_id INTEGER, " + "group_id INTEGER, " + "UNIQUE (org_id, group_id))"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + if (!pgsql_index_exists (db, "orggroup_groupid_idx")) { + sql = "CREATE INDEX orggroup_groupid_idx ON OrgGroup (group_id)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + } + } + + return 0; +} + +int ccnet_org_manager_create_org (CcnetOrgManager *mgr, + const char *org_name, + const char *url_prefix, + const char *creator, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + gint64 now = get_current_time(); + int rc; + + rc = ccnet_db_statement_query (db, + "INSERT INTO Organization(org_name, url_prefix," + " creator, ctime) VALUES (?, ?, ?, ?)", + 4, "string", org_name, "string", url_prefix, + "string", creator, "int64", now); + + if (rc < 0) { + g_set_error (error, CCNET_DOMAIN, 0, "Failed to create organization"); + return -1; + } + + int org_id = ccnet_db_statement_get_int (db, + "SELECT org_id FROM Organization WHERE " + "url_prefix = ?", 1, "string", url_prefix); + if (org_id < 0) { + g_set_error (error, CCNET_DOMAIN, 0, "Failed to create organization"); + return -1; + } + + rc = ccnet_db_statement_query (db, "INSERT INTO OrgUser values (?, ?, ?)", + 3, "int", org_id, "string", creator, "int", 1); + if (rc < 0) { + ccnet_db_statement_query (db, "DELETE FROM Organization WHERE org_id=?", + 1, "int", org_id); + g_set_error (error, CCNET_DOMAIN, 0, "Failed to create organization"); + return -1; + } + + return org_id; +} + +int +ccnet_org_manager_remove_org (CcnetOrgManager *mgr, + int org_id, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + + ccnet_db_statement_query (db, "DELETE FROM Organization WHERE org_id = ?", + 1, "int", org_id); + + ccnet_db_statement_query (db, "DELETE FROM OrgUser WHERE org_id = %d", + 1, "int", org_id); + + ccnet_db_statement_query (db, "DELETE FROM OrgGroup WHERE org_id = %d", + 1, "int", org_id); + + return 0; +} + + +static gboolean +get_all_orgs_cb (CcnetDBRow *row, void *data) +{ + GList **p_list = data; + CcnetOrganization *org = NULL; + int org_id; + const char *org_name; + const char *url_prefix; + const char *creator; + gint64 ctime; + + org_id = ccnet_db_row_get_column_int (row, 0); + org_name = ccnet_db_row_get_column_text (row, 1); + url_prefix = ccnet_db_row_get_column_text (row, 2); + creator = ccnet_db_row_get_column_text (row, 3); + ctime = ccnet_db_row_get_column_int64 (row, 4); + + org = g_object_new (CCNET_TYPE_ORGANIZATION, + "org_id", org_id, + "org_name", org_name, + "url_prefix", url_prefix, + "creator", creator, + "ctime", ctime, + NULL); + + *p_list = g_list_prepend (*p_list, org); + + return TRUE; +} + +GList * +ccnet_org_manager_get_all_orgs (CcnetOrgManager *mgr, + int start, + int limit) +{ + CcnetDB *db = mgr->priv->db; + char *sql; + GList *ret = NULL; + int rc; + + if (start == -1 && limit == -1) { + sql = "SELECT * FROM Organization ORDER BY org_id"; + rc = ccnet_db_statement_foreach_row (db, sql, get_all_orgs_cb, &ret, 0); + } else { + sql = "SELECT * FROM Organization ORDER BY org_id LIMIT ? OFFSET ?"; + rc = ccnet_db_statement_foreach_row (db, sql, get_all_orgs_cb, &ret, + 2, "int", limit, "int", start); + } + + if (rc < 0) + return NULL; + + return g_list_reverse (ret); +} + +int +ccnet_org_manager_count_orgs (CcnetOrgManager *mgr) +{ + CcnetDB *db = mgr->priv->db; + char *sql; + gint64 ret; + + sql = "SELECT count(*) FROM Organization"; + + ret = ccnet_db_get_int64 (db, sql); + if (ret < 0) + return -1; + return ret; +} + +static gboolean +get_org_cb (CcnetDBRow *row, void *data) +{ + CcnetOrganization **p_org = data; + int org_id; + const char *org_name; + const char *url_prefix; + const char *creator; + gint64 ctime; + + org_id = ccnet_db_row_get_column_int (row, 0); + org_name = ccnet_db_row_get_column_text (row, 1); + url_prefix = ccnet_db_row_get_column_text (row, 2); + creator = ccnet_db_row_get_column_text (row, 3); + ctime = ccnet_db_row_get_column_int64 (row, 4); + + *p_org = g_object_new (CCNET_TYPE_ORGANIZATION, + "org_id", org_id, + "org_name", org_name, + "url_prefix", url_prefix, + "creator", creator, + "ctime", ctime, + NULL); + return FALSE; +} + +CcnetOrganization * +ccnet_org_manager_get_org_by_url_prefix (CcnetOrgManager *mgr, + const char *url_prefix, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + char *sql; + CcnetOrganization *org = NULL; + + sql = "SELECT org_id, org_name, url_prefix, creator," + " ctime FROM Organization WHERE url_prefix = ?"; + + if (ccnet_db_statement_foreach_row (db, sql, get_org_cb, &org, + 1, "string", url_prefix) < 0) { + return NULL; + } + + return org; +} + +CcnetOrganization * +ccnet_org_manager_get_org_by_id (CcnetOrgManager *mgr, + int org_id, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + char *sql; + CcnetOrganization *org = NULL; + + sql = "SELECT org_id, org_name, url_prefix, creator," + " ctime FROM Organization WHERE org_id = ?"; + + if (ccnet_db_statement_foreach_row (db, sql, get_org_cb, &org, + 1, "int", org_id) < 0) { + return NULL; + } + + return org; +} + +int +ccnet_org_manager_add_org_user (CcnetOrgManager *mgr, + int org_id, + const char *email, + int is_staff, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + + return ccnet_db_statement_query (db, "INSERT INTO OrgUser values (?, ?, ?)", + 3, "int", org_id, "string", email, + "int", is_staff); +} + +int +ccnet_org_manager_remove_org_user (CcnetOrgManager *mgr, + int org_id, + const char *email, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + + return ccnet_db_statement_query (db, "DELETE FROM OrgUser WHERE org_id=? AND " + "email=?", 2, "int", org_id, "string", email); +} + +static gboolean +get_orgs_by_user_cb (CcnetDBRow *row, void *data) +{ + GList **p_list = (GList **)data; + CcnetOrganization *org = NULL; + int org_id; + const char *email; + int is_staff; + const char *org_name; + const char *url_prefix; + const char *creator; + gint64 ctime; + + org_id = ccnet_db_row_get_column_int (row, 0); + email = (char *) ccnet_db_row_get_column_text (row, 1); + is_staff = ccnet_db_row_get_column_int (row, 2); + org_name = (char *) ccnet_db_row_get_column_text (row, 3); + url_prefix = (char *) ccnet_db_row_get_column_text (row, 4); + creator = (char *) ccnet_db_row_get_column_text (row, 5); + ctime = ccnet_db_row_get_column_int64 (row, 6); + + org = g_object_new (CCNET_TYPE_ORGANIZATION, + "org_id", org_id, + "email", email, + "is_staff", is_staff, + "org_name", org_name, + "url_prefix", url_prefix, + "creator", creator, + "ctime", ctime, + NULL); + *p_list = g_list_prepend (*p_list, org); + + return TRUE; +} + +GList * +ccnet_org_manager_get_orgs_by_user (CcnetOrgManager *mgr, + const char *email, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + char *sql; + GList *ret = NULL; + + sql = "SELECT t1.org_id, email, is_staff, org_name," + " url_prefix, creator, ctime FROM OrgUser t1, Organization t2" + " WHERE t1.org_id = t2.org_id AND email = ?"; + + if (ccnet_db_statement_foreach_row (db, sql, get_orgs_by_user_cb, &ret, + 1, "string", email) < 0) { + g_list_free (ret); + return NULL; + } + + return g_list_reverse (ret); +} + +static gboolean +get_org_emailusers (CcnetDBRow *row, void *data) +{ + GList **list = (GList **)data; + const char *email = (char *) ccnet_db_row_get_column_text (row, 0); + + *list = g_list_prepend (*list, g_strdup (email)); + return TRUE; +} + +GList * +ccnet_org_manager_get_org_emailusers (CcnetOrgManager *mgr, + const char *url_prefix, + int start, int limit) +{ + CcnetDB *db = mgr->priv->db; + char *sql; + GList *ret = NULL; + int rc; + + if (start == -1 && limit == -1) { + sql = "SELECT email FROM OrgUser WHERE org_id =" + " (SELECT org_id FROM Organization WHERE url_prefix = ?)" + " ORDER BY email"; + rc = ccnet_db_statement_foreach_row (db, sql, get_org_emailusers, &ret, + 1, "string", url_prefix); + } else { + sql = "SELECT email FROM OrgUser WHERE org_id =" + " (SELECT org_id FROM Organization WHERE url_prefix = ?)" + " ORDER BY email LIMIT ? OFFSET ?"; + rc = ccnet_db_statement_foreach_row (db, sql, get_org_emailusers, &ret, + 3, "string", url_prefix, + "int", limit, "int", start); + } + + if (rc < 0) + return NULL; + + return g_list_reverse (ret); +} + +int +ccnet_org_manager_add_org_group (CcnetOrgManager *mgr, + int org_id, + int group_id, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + + return ccnet_db_statement_query (db, "INSERT INTO OrgGroup VALUES (?, ?)", + 2, "int", org_id, "int", group_id); +} + +int +ccnet_org_manager_remove_org_group (CcnetOrgManager *mgr, + int org_id, + int group_id, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + + return ccnet_db_statement_query (db, "DELETE FROM OrgGroup WHERE org_id=?" + " AND group_id=?", + 2, "int", org_id, "string", group_id); +} + +int +ccnet_org_manager_is_org_group (CcnetOrgManager *mgr, + int group_id, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + + return ccnet_db_statement_exists (db, "SELECT group_id FROM OrgGroup " + "WHERE group_id = ?", 1, "int", group_id); +} + +int +ccnet_org_manager_get_org_id_by_group (CcnetOrgManager *mgr, + int group_id, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + char *sql; + + sql = "SELECT org_id FROM OrgGroup WHERE group_id = ?"; + return ccnet_db_statement_get_int (db, sql, 1, "int", group_id); +} + +static gboolean +get_org_groups (CcnetDBRow *row, void *data) +{ + GList **plist = data; + + int group_id = ccnet_db_row_get_column_int (row, 0); + + *plist = g_list_prepend (*plist, (gpointer)(long)group_id); + + return TRUE; +} + +GList * +ccnet_org_manager_get_org_groups (CcnetOrgManager *mgr, + int org_id, + int start, + int limit) +{ + CcnetDB *db = mgr->priv->db; + GList *ret = NULL; + int rc; + + if (limit == -1) { + rc = ccnet_db_statement_foreach_row (db, + "SELECT group_id FROM OrgGroup WHERE " + "org_id = ?", + get_org_groups, &ret, + 1, "int", org_id); + } else { + rc = ccnet_db_statement_foreach_row (db, + "SELECT group_id FROM OrgGroup WHERE " + "org_id = ? LIMIT ?, ?", + get_org_groups, &ret, + 3, "int", org_id, "int", start, + "int", limit); + } + + if (rc < 0) { + g_list_free (ret); + return NULL; + } + + return g_list_reverse (ret); +} + +int +ccnet_org_manager_org_user_exists (CcnetOrgManager *mgr, + int org_id, + const char *email, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + + return ccnet_db_statement_exists (db, "SELECT org_id FROM OrgUser WHERE " + "org_id = ? AND email = ?", + 2, "int", org_id, "string", email); +} + +char * +ccnet_org_manager_get_url_prefix_by_org_id (CcnetOrgManager *mgr, + int org_id, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + char *sql; + + sql = "SELECT url_prefix FROM Organization WHERE org_id = ?"; + + return ccnet_db_statement_get_string (db, sql, 1, "int", org_id); +} + +int +ccnet_org_manager_is_org_staff (CcnetOrgManager *mgr, + int org_id, + const char *email, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + char *sql; + + sql = "SELECT is_staff FROM OrgUser WHERE org_id=? AND email=?"; + + return ccnet_db_statement_get_int (db, sql, 2, "int", org_id, "string", email); +} + +int +ccnet_org_manager_set_org_staff (CcnetOrgManager *mgr, + int org_id, + const char *email, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + + return ccnet_db_statement_query (db, "UPDATE OrgUser SET is_staff = 1 " + "WHERE org_id=? AND email=?", 2, + "int", org_id, "string", email); +} + +int +ccnet_org_manager_unset_org_staff (CcnetOrgManager *mgr, + int org_id, + const char *email, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + + return ccnet_db_statement_query (db, "UPDATE OrgUser SET is_staff = 0 " + "WHERE org_id=? AND email=?", 2, + "int", org_id, "string", email); +} + +int +ccnet_org_manager_set_org_name(CcnetOrgManager *mgr, + int org_id, + const char *org_name, + GError **error) +{ + CcnetDB *db = mgr->priv->db; + + return ccnet_db_statement_query (db, + "UPDATE `Organization` set org_name = ? " + "WHERE org_id = ?", + 2, "string", org_name, "int", org_id); + return 0; +} + diff --git a/net/server/org-mgr.h b/net/server/org-mgr.h new file mode 100644 index 0000000..f60cefc --- /dev/null +++ b/net/server/org-mgr.h @@ -0,0 +1,142 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef _ORG_MGR_H_ +#define _ORG_MGR_H_ + +#include "../common/session.h" + +typedef struct _CcnetOrgManager CcnetOrgManager; +typedef struct _CcnetOrgManagerPriv CcnetOrgManagerPriv; + +struct _CcnetOrgManager +{ + CcnetSession *session; + + CcnetOrgManagerPriv *priv; +}; + +CcnetOrgManager* ccnet_org_manager_new (CcnetSession *session); + +int +ccnet_org_manager_prepare (CcnetOrgManager *manager); + +void +ccnet_org_manager_start (CcnetOrgManager *manager); + +int +ccnet_org_manager_create_org (CcnetOrgManager *mgr, + const char *org_name, + const char *url_prefix, + const char *creator, + GError **error); + +int +ccnet_org_manager_remove_org (CcnetOrgManager *mgr, + int org_id, + GError **error); + +GList * +ccnet_org_manager_get_all_orgs (CcnetOrgManager *mgr, + int start, + int limit); + +int +ccnet_org_manager_count_orgs (CcnetOrgManager *mgr); + +CcnetOrganization * +ccnet_org_manager_get_org_by_url_prefix (CcnetOrgManager *mgr, + const char *url_prefix, + GError **error); + +CcnetOrganization * +ccnet_org_manager_get_org_by_id (CcnetOrgManager *mgr, + int org_id, + GError **error); + +int +ccnet_org_manager_add_org_user (CcnetOrgManager *mgr, + int org_id, + const char *email, + int is_staff, + GError **error); + +int +ccnet_org_manager_remove_org_user (CcnetOrgManager *mgr, + int org_id, + const char *email, + GError **error); + +GList * +ccnet_org_manager_get_orgs_by_user (CcnetOrgManager *mgr, + const char *email, + GError **error); + +GList * +ccnet_org_manager_get_org_emailusers (CcnetOrgManager *mgr, + const char *url_prefix, + int start, int limit); + +int +ccnet_org_manager_add_org_group (CcnetOrgManager *mgr, + int org_id, + int group_id, + GError **error); +int +ccnet_org_manager_remove_org_group (CcnetOrgManager *mgr, + int org_id, + int group_id, + GError **error); + +int +ccnet_org_manager_is_org_group (CcnetOrgManager *mgr, + int group_id, + GError **error); + +int +ccnet_org_manager_get_org_id_by_group (CcnetOrgManager *mgr, + int group_id, + GError **error); + +GList * +ccnet_org_manager_get_org_groups (CcnetOrgManager *mgr, + int org_id, + int start, + int limit); + +int +ccnet_org_manager_org_user_exists (CcnetOrgManager *mgr, + int org_id, + const char *email, + GError **error); + +char * +ccnet_org_manager_get_url_prefix_by_org_id (CcnetOrgManager *mgr, + int org_id, + GError **error); + +int +ccnet_org_manager_is_org_staff (CcnetOrgManager *mgr, + int org_id, + const char *email, + GError **error); + +int +ccnet_org_manager_set_org_staff (CcnetOrgManager *mgr, + int org_id, + const char *email, + GError **error); + +int +ccnet_org_manager_unset_org_staff (CcnetOrgManager *mgr, + int org_id, + const char *email, + GError **error); + +int +ccnet_org_manager_set_org_name(CcnetOrgManager *mgr, + int org_id, + const char *org_name, + GError **error); + + +#endif /* _ORG_MGR_H_ */ diff --git a/net/server/processors/recvlogin-proc.c b/net/server/processors/recvlogin-proc.c new file mode 100644 index 0000000..c77afbd --- /dev/null +++ b/net/server/processors/recvlogin-proc.c @@ -0,0 +1,115 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include "session.h" +#include "processor.h" +#include "log.h" +#include "peer.h" +#include "peer-mgr.h" +#include "user-mgr.h" +#include "recvlogin-proc.h" +#include "server-session.h" + +#define SC_ERR_WRONG_PASSWD "301" +#define SS_ERR_WRONG_PASSWD "wrong password" + +#define SC_INTERNAL_ERROR "302" +#define SS_INTERNAL_ERROR "relay internal error" + + +G_DEFINE_TYPE (CcnetRecvloginProc, ccnet_recvlogin_proc, CCNET_TYPE_PROCESSOR) + +static int start (CcnetProcessor *processor, int argc, char **argv); +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void +release_resource(CcnetProcessor *processor) +{ + /* FILL IT */ + + CCNET_PROCESSOR_CLASS (ccnet_recvlogin_proc_parent_class)->release_resource (processor); +} + + +static void +ccnet_recvlogin_proc_class_init (CcnetRecvloginProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->start = start; + proc_class->handle_update = handle_update; + proc_class->release_resource = release_resource; +} + +static void +ccnet_recvlogin_proc_init (CcnetRecvloginProc *processor) +{ +} + +static void +check_emailuser (CcnetProcessor *processor, + const char *email, const char *passwd) +{ + char *prev_email; + CcnetUserManager *user_mgr = + ((CcnetServerSession *)processor->session)->user_mgr; + + prev_email = ccnet_user_manager_get_binding_email (user_mgr, + processor->peer->id); + if (prev_email) { + /* This peer id has already been binded to some email address. */ + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + + } else if (!ccnet_user_manager_validate_emailuser (user_mgr, + email, passwd)) { + + ccnet_processor_send_response (processor, SC_ERR_WRONG_PASSWD, + SS_ERR_WRONG_PASSWD, NULL, 0); + + } else { + CcnetPeer *peer = processor->peer; + /* ccnet_peer_manager_add_role (session->peer_mgr, peer, "MyClient"); */ + /* ccnet_debug ("add role 'MyClient' for peer %.10s\n", peer->id); */ + if (ccnet_user_manager_add_binding (user_mgr, email, + peer->id) < 0) { + ccnet_warning ("Failed to add binding for email(%s), user(%.10s)\n", + email, peer->id); + + ccnet_processor_send_response (processor, + SC_INTERNAL_ERROR, + SS_INTERNAL_ERROR, + NULL, 0); + } + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + } + g_free (prev_email); +} + +static int +start (CcnetProcessor *processor, int argc, char **argv) +{ + if (argc != 2 || !argv[0] || !argv[1]) { + ccnet_processor_error (processor, SC_BAD_ARGS, SS_BAD_ARGS); + return -1; + } + char *email = argv[0]; + char *passwd = argv[1]; + /* ccnet_message ("receive login info from %s : email(%s), passwd(%s)\n", */ + /* processor->peer->id, email, passwd); */ + + check_emailuser (processor, email, passwd); + ccnet_processor_done(processor, TRUE); + + return 0; +} + + +static void +handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ +} diff --git a/net/server/processors/recvlogin-proc.h b/net/server/processors/recvlogin-proc.h new file mode 100644 index 0000000..6a2963e --- /dev/null +++ b/net/server/processors/recvlogin-proc.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_RECVLOGIN_PROC_H +#define CCNET_RECVLOGIN_PROC_H + +#include + + +#define CCNET_TYPE_RECVLOGIN_PROC (ccnet_recvlogin_proc_get_type ()) +#define CCNET_RECVLOGIN_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_RECVLOGIN_PROC, CcnetRecvloginProc)) +#define CCNET_IS_RECVLOGIN_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_RECVLOGIN_PROC)) +#define CCNET_RECVLOGIN_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_RECVLOGIN_PROC, CcnetRecvloginProcClass)) +#define IS_CCNET_RECVLOGIN_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_RECVLOGIN_PROC)) +#define CCNET_RECVLOGIN_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_RECVLOGIN_PROC, CcnetRecvloginProcClass)) + +typedef struct _CcnetRecvloginProc CcnetRecvloginProc; +typedef struct _CcnetRecvloginProcClass CcnetRecvloginProcClass; + +struct _CcnetRecvloginProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetRecvloginProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_recvlogin_proc_get_type (); + +#endif + diff --git a/net/server/processors/recvlogout-proc.c b/net/server/processors/recvlogout-proc.c new file mode 100644 index 0000000..9d1b362 --- /dev/null +++ b/net/server/processors/recvlogout-proc.c @@ -0,0 +1,83 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "recvlogout-proc.h" + +#include "session.h" +#include "processor.h" +#include "log.h" +#include "peer.h" +#include "peer-mgr.h" +#include "user-mgr.h" +#include "server-session.h" + +G_DEFINE_TYPE (CcnetRecvlogoutProc, ccnet_recvlogout_proc, CCNET_TYPE_PROCESSOR) + +#define SC_NO_BINDING "301" +#define SS_NO_BINDING "Not binded yet" + +static int start (CcnetProcessor *processor, int argc, char **argv); +static void handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen); + +static void +release_resource(CcnetProcessor *processor) +{ + /* FILL IT */ + + CCNET_PROCESSOR_CLASS (ccnet_recvlogout_proc_parent_class)->release_resource (processor); +} + + +static void +ccnet_recvlogout_proc_class_init (CcnetRecvlogoutProcClass *klass) +{ + CcnetProcessorClass *proc_class = CCNET_PROCESSOR_CLASS (klass); + + proc_class->start = start; + proc_class->handle_update = handle_update; + proc_class->release_resource = release_resource; +} + +static void +ccnet_recvlogout_proc_init (CcnetRecvlogoutProc *processor) +{ +} + +static int +start (CcnetProcessor *processor, int argc, char **argv) +{ + CcnetUserManager *user_mgr = + ((CcnetServerSession *)processor->session)->user_mgr; + + if (argc != 0) { + ccnet_processor_error (processor, SC_BAD_ARGS, SS_BAD_ARGS); + return -1; + } + + CcnetPeer *peer = processor->peer; + char *email; + + /* ccnet_peer_manager_remove_role (session->peer_mgr, peer, "MyClient"); */ + + email = ccnet_user_manager_get_binding_email (user_mgr, peer->id); + if (!email) { + ccnet_processor_send_response (processor, SC_NO_BINDING, + SS_NO_BINDING, NULL, 0); + } else { + ccnet_user_manager_remove_one_binding (user_mgr, + email, peer->id); + ccnet_processor_send_response (processor, SC_OK, SS_OK, NULL, 0); + } + + g_free (email); + ccnet_processor_done (processor, TRUE); + return 0; +} + +static void +handle_update (CcnetProcessor *processor, + char *code, char *code_msg, + char *content, int clen) +{ +} diff --git a/net/server/processors/recvlogout-proc.h b/net/server/processors/recvlogout-proc.h new file mode 100644 index 0000000..10029ef --- /dev/null +++ b/net/server/processors/recvlogout-proc.h @@ -0,0 +1,31 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_RECVLOGOUT_PROC_H +#define CCNET_RECVLOGOUT_PROC_H + +#include +#include + + +#define CCNET_TYPE_RECVLOGOUT_PROC (ccnet_recvlogout_proc_get_type ()) +#define CCNET_RECVLOGOUT_PROC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_RECVLOGOUT_PROC, CcnetRecvlogoutProc)) +#define CCNET_IS_RECVLOGOUT_PROC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_RECVLOGOUT_PROC)) +#define CCNET_RECVLOGOUT_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_RECVLOGOUT_PROC, CcnetRecvlogoutProcClass)) +#define IS_CCNET_RECVLOGOUT_PROC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_RECVLOGOUT_PROC)) +#define CCNET_RECVLOGOUT_PROC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_RECVLOGOUT_PROC, CcnetRecvlogoutProcClass)) + +typedef struct _CcnetRecvlogoutProc CcnetRecvlogoutProc; +typedef struct _CcnetRecvlogoutProcClass CcnetRecvlogoutProcClass; + +struct _CcnetRecvlogoutProc { + CcnetProcessor parent_instance; +}; + +struct _CcnetRecvlogoutProcClass { + CcnetProcessorClass parent_class; +}; + +GType ccnet_recvlogout_proc_get_type (); + +#endif + diff --git a/net/server/server-session.c b/net/server/server-session.c new file mode 100644 index 0000000..24cd4aa --- /dev/null +++ b/net/server/server-session.c @@ -0,0 +1,257 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include +#include +#include +#include + +#include "getgateway.h" +#include "utils.h" +#include "net.h" +#include "rsa.h" + +#include "server-session.h" +#include "peer.h" +#include "peer-mgr.h" +#include "perm-mgr.h" +#include "packet-io.h" +#include "connect-mgr.h" +#include "message.h" +#include "message-manager.h" +#include "proc-factory.h" +#include "algorithms.h" +#include "ccnet-config.h" +#include "user-mgr.h" +#include "group-mgr.h" +#include "org-mgr.h" +#include "job-mgr.h" + +#define DEBUG_FLAG CCNET_DEBUG_OTHER +#include "log.h" +#define CCNET_DB "ccnet.db" + +G_DEFINE_TYPE (CcnetServerSession, ccnet_server_session, CCNET_TYPE_SESSION); + +static int load_database_config (CcnetSession *session); +static int server_session_prepare (CcnetSession *session); +static void server_session_start (CcnetSession *session); +static void on_peer_auth_done (CcnetSession *session, CcnetPeer *peer); + + +static void +ccnet_server_session_class_init (CcnetServerSessionClass *klass) +{ + CcnetSessionClass *session_class = CCNET_SESSION_CLASS (klass); + + session_class->prepare = server_session_prepare; + session_class->start = server_session_start; + session_class->on_peer_auth_done = on_peer_auth_done; +} + +static void +ccnet_server_session_init (CcnetServerSession *server_session) +{ + CcnetSession *session = (CcnetSession *)server_session; + server_session->user_mgr = ccnet_user_manager_new (session); + server_session->group_mgr = ccnet_group_manager_new (session); + server_session->org_mgr = ccnet_org_manager_new (session); +} + +CcnetServerSession * +ccnet_server_session_new (const char *config_dir_r) +{ + return g_object_new (CCNET_TYPE_SERVER_SESSION, NULL); +} + +int +server_session_prepare (CcnetSession *session) +{ + CcnetServerSession *server_session = (CcnetServerSession *)session; + char *service_url = NULL; + + service_url = ccnet_key_file_get_string (session->keyf, "General", "SERVICE_URL"); + session->base.service_url = service_url; + + if (load_database_config (session) < 0) { + ccnet_warning ("Failed to load database config.\n"); + return -1; + } + + /* */ + char *enc; + enc = ccnet_key_file_get_string (session->keyf, "NETWORK", "ENCRYPT_CHANNEL"); + if (enc && g_ascii_strncasecmp(enc, "false", 5) == 0) + session->encrypt_channel = 0; + else + /* encrypt channel on default */ + session->encrypt_channel = 1; + + if (ccnet_user_manager_prepare (server_session->user_mgr) < 0) + return -1; + + if (ccnet_group_manager_prepare (server_session->group_mgr) < 0) + return -1; + + if (ccnet_org_manager_prepare (server_session->org_mgr) < 0) + return -1; + + return 0; +} + +void +server_session_start (CcnetSession *session) +{ + g_signal_connect (session->peer_mgr, "peer-auth-done", + G_CALLBACK(on_peer_auth_done), NULL); +} + +#define DEFAULT_MAX_CONNECTIONS 100 + +static int init_sqlite_database (CcnetSession *session) +{ + char *db_path; + + db_path = g_build_path ("/", session->config_dir, CCNET_DB, NULL); + session->db = ccnet_db_new_sqlite (db_path); + if (!session->db) { + g_warning ("Failed to open database.\n"); + return -1; + } + return 0; +} + +#define MYSQL_DEFAULT_PORT 3306 + +static int init_mysql_database (CcnetSession *session) +{ + char *host, *user, *passwd, *db, *unix_socket, *charset; + int port; + gboolean use_ssl = FALSE; + int max_connections = 0; + + host = ccnet_key_file_get_string (session->keyf, "Database", "HOST"); + user = ccnet_key_file_get_string (session->keyf, "Database", "USER"); + passwd = ccnet_key_file_get_string (session->keyf, "Database", "PASSWD"); + db = ccnet_key_file_get_string (session->keyf, "Database", "DB"); + + if (!host) { + g_warning ("DB host not set in config.\n"); + return -1; + } + if (!user) { + g_warning ("DB user not set in config.\n"); + return -1; + } + if (!passwd) { + g_warning ("DB passwd not set in config.\n"); + return -1; + } + if (!db) { + g_warning ("DB name not set in config.\n"); + return -1; + } + + GError *error = NULL; + port = g_key_file_get_integer (session->keyf, "Database", "PORT", &error); + if (error) { + g_clear_error (&error); + port = MYSQL_DEFAULT_PORT; + } + + unix_socket = ccnet_key_file_get_string (session->keyf, + "Database", "UNIX_SOCKET"); + use_ssl = g_key_file_get_boolean (session->keyf, "Database", "USE_SSL", NULL); + charset = ccnet_key_file_get_string (session->keyf, + "Database", "CONNECTION_CHARSET"); + + max_connections = g_key_file_get_integer (session->keyf, + "Database", "MAX_CONNECTIONS", + NULL); + if (max_connections <= 0) + max_connections = DEFAULT_MAX_CONNECTIONS; + + session->db = ccnet_db_new_mysql (host, port, user, passwd, db, unix_socket, use_ssl, charset, max_connections); + if (!session->db) { + g_warning ("Failed to open database.\n"); + return -1; + } + + g_free (host); + g_free (user); + g_free (passwd); + g_free (db); + g_free (unix_socket); + g_free (charset); + + return 0; +} + +static int init_pgsql_database (CcnetSession *session) +{ + char *host, *user, *passwd, *db, *unix_socket; + + host = ccnet_key_file_get_string (session->keyf, "Database", "HOST"); + user = ccnet_key_file_get_string (session->keyf, "Database", "USER"); + passwd = ccnet_key_file_get_string (session->keyf, "Database", "PASSWD"); + db = ccnet_key_file_get_string (session->keyf, "Database", "DB"); + + if (!host) { + g_warning ("DB host not set in config.\n"); + return -1; + } + if (!user) { + g_warning ("DB user not set in config.\n"); + return -1; + } + if (!passwd) { + g_warning ("DB passwd not set in config.\n"); + return -1; + } + if (!db) { + g_warning ("DB name not set in config.\n"); + return -1; + } + unix_socket = ccnet_key_file_get_string (session->keyf, + "Database", "UNIX_SOCKET"); + + session->db = ccnet_db_new_pgsql (host, user, passwd, db, unix_socket); + if (!session->db) { + g_warning ("Failed to open database.\n"); + return -1; + } + + return 0; +} + +static int +load_database_config (CcnetSession *session) +{ + int ret; + char *engine; + + engine = ccnet_key_file_get_string (session->keyf, "Database", "ENGINE"); + if (!engine || strncasecmp (engine, DB_SQLITE, sizeof(DB_SQLITE)) == 0) { + ccnet_debug ("Use database sqlite\n"); + ret = init_sqlite_database (session); + } else if (strncasecmp (engine, DB_MYSQL, sizeof(DB_MYSQL)) == 0) { + ccnet_debug ("Use database Mysql\n"); + ret = init_mysql_database (session); + } else if (strncasecmp (engine, DB_PGSQL, sizeof(DB_PGSQL)) == 0) { + ccnet_debug ("Use database PostgreSQL\n"); + ret = init_pgsql_database (session); + } else { + ccnet_warning ("Unknown database type: %s.\n", engine); + ret = -1; + } + + return ret; +} + + +static void +on_peer_auth_done (CcnetSession *session, CcnetPeer *peer) +{ + ccnet_peer_manager_send_ready_message (session->peer_mgr, peer); +} diff --git a/net/server/server-session.h b/net/server/server-session.h new file mode 100644 index 0000000..e4e2c1e --- /dev/null +++ b/net/server/server-session.h @@ -0,0 +1,37 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_SERVER_SESSION_H +#define CCNET_SERVER_SESSION_H + +#include + +#define CCNET_TYPE_SERVER_SESSION (ccnet_server_session_get_type ()) +#define CCNET_SERVER_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_SERVER_SESSION, CcnetServerSession)) +#define CCNET_IS_SERVER_SESSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_SERVER_SESSION)) +#define CCNET_SERVER_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_SERVER_SESSION, CcnetServerSessionClass)) +#define CCNET_IS_SERVER_SESSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_SERVER_SESSION)) +#define CCNET_SERVER_SESSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_SERVER_SESSION, CcnetServerSessionClass)) + +typedef struct CcnetServerSession CcnetServerSession; +typedef struct _CcnetServerSessionClass CcnetServerSessionClass; + +struct CcnetServerSession +{ + CcnetSession common_session; + + struct _CcnetUserManager *user_mgr; + struct _CcnetGroupManager *group_mgr; + struct _CcnetOrgManager *org_mgr; +}; + +struct _CcnetServerSessionClass +{ + CcnetSessionClass parent_class; +}; + +GType ccnet_server_session_get_type (); + +CcnetServerSession *ccnet_server_session_new (); + + +#endif diff --git a/net/server/user-mgr.c b/net/server/user-mgr.c new file mode 100644 index 0000000..7a567de --- /dev/null +++ b/net/server/user-mgr.c @@ -0,0 +1,1691 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include "common.h" + +#include +#include + +#include "ccnet-db.h" +#include "timer.h" +#include "utils.h" + + +#include "peer.h" +#include "session.h" +#include "peer-mgr.h" +#include "user-mgr.h" + +#include +#include +#include + +#ifdef HAVE_LDAP + #ifndef WIN32 + #define LDAP_DEPRECATED 1 + #include + #else + #include + #endif +#endif + +#define DEBUG_FLAG CCNET_DEBUG_PEER +#include "log.h" + +#define DEFAULT_SAVING_INTERVAL_MSEC 30000 + + +G_DEFINE_TYPE (CcnetUserManager, ccnet_user_manager, G_TYPE_OBJECT); + + +#define GET_PRIV(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), CCNET_TYPE_USER_MANAGER, CcnetUserManagerPriv)) + + +static int open_db (CcnetUserManager *manager); + +#ifdef HAVE_LDAP +static int try_load_ldap_settings (CcnetUserManager *manager); +#endif + +struct CcnetUserManagerPriv { + CcnetDB *db; + int max_users; +}; + +static void +ccnet_user_manager_class_init (CcnetUserManagerClass *klass) +{ + + g_type_class_add_private (klass, sizeof (CcnetUserManagerPriv)); +} + +static void +ccnet_user_manager_init (CcnetUserManager *manager) +{ + manager->priv = GET_PRIV(manager); +} + +CcnetUserManager* +ccnet_user_manager_new (CcnetSession *session) +{ + CcnetUserManager* manager; + + manager = g_object_new (CCNET_TYPE_USER_MANAGER, NULL); + manager->session = session; + manager->user_hash = g_hash_table_new (g_str_hash, g_str_equal); + + return manager; +} + +#define DEFAULT_PASSWD_HASH_ITER 10000 + +// return current active user number +static int +get_current_user_number (CcnetUserManager *manager) +{ + int total = 0, count; + + count = ccnet_user_manager_count_emailusers (manager, "DB"); + if (count < 0) { + ccnet_warning ("Failed to get user number from DB.\n"); + return -1; + } + total += count; + +#ifdef HAVE_LDAP + if (manager->use_ldap) { + count = ccnet_user_manager_count_emailusers (manager, "LDAP"); + if (count < 0) { + ccnet_warning ("Failed to get user number from LDAP.\n"); + return -1; + } + total += count; + } +#endif + + return total; +} + +static gboolean +check_user_number (CcnetUserManager *manager, gboolean allow_equal) +{ + if (manager->priv->max_users == 0) { + return TRUE; + } + + int cur_num = get_current_user_number (manager); + if (cur_num < 0) { + return FALSE; + } + + if ((allow_equal && cur_num > manager->priv->max_users) || + (!allow_equal && cur_num >= manager->priv->max_users)) { + ccnet_warning ("The number of users exceeds limit, max %d, current %d\n", + manager->priv->max_users, cur_num); + return FALSE; + } + + return TRUE; +} + +int +ccnet_user_manager_prepare (CcnetUserManager *manager) +{ + int ret; + +#ifdef HAVE_LDAP + if (try_load_ldap_settings (manager) < 0) + return -1; +#endif + + int iter = g_key_file_get_integer (manager->session->keyf, + "USER", "PASSWORD_HASH_ITERATIONS", + NULL); + if (iter <= 0) + iter = DEFAULT_PASSWD_HASH_ITER; + manager->passwd_hash_iter = iter; + + manager->userdb_path = g_build_filename (manager->session->config_dir, + "user-db", NULL); + ret = open_db(manager); + if (ret < 0) + return ret; + + if (!check_user_number (manager, TRUE)) { + return -1; + } + + return 0; +} + +void +ccnet_user_manager_free (CcnetUserManager *manager) +{ + g_object_unref (manager); +} + +void +ccnet_user_manager_start (CcnetUserManager *manager) +{ + +} + +void ccnet_user_manager_on_exit (CcnetUserManager *manager) +{ +} + +void +ccnet_user_manager_set_max_users (CcnetUserManager *manager, gint64 max_users) +{ + manager->priv->max_users = max_users; +} + +/* -------- LDAP related --------- */ + +#ifdef HAVE_LDAP + + +static int try_load_ldap_settings (CcnetUserManager *manager) +{ + GKeyFile *config = manager->session->keyf; + + manager->ldap_host = g_key_file_get_string (config, "LDAP", "HOST", NULL); + if (!manager->ldap_host) + return 0; + + manager->use_ldap = TRUE; + +#ifdef WIN32 + manager->use_ssl = g_key_file_get_boolean (config, "LDAP", "USE_SSL", NULL); +#endif + + char *base_list = g_key_file_get_string (config, "LDAP", "BASE", NULL); + if (!base_list) { + ccnet_warning ("LDAP: BASE not found in config file.\n"); + return -1; + } + manager->base_list = g_strsplit (base_list, ";", -1); + + manager->filter = g_key_file_get_string (config, "LDAP", "FILTER", NULL); + + manager->user_dn = g_key_file_get_string (config, "LDAP", "USER_DN", NULL); + if (manager->user_dn) { + manager->password = g_key_file_get_string (config, "LDAP", "PASSWORD", NULL); + if (!manager->password) { + ccnet_warning ("LDAP: PASSWORD not found in config file.\n"); + return -1; + } + } + /* Use anonymous if user_dn is not set. */ + + manager->login_attr = g_key_file_get_string (config, "LDAP", "LOGIN_ATTR", NULL); + if (!manager->login_attr) + manager->login_attr = g_strdup("mail"); + + return 0; +} + +static LDAP *ldap_init_and_bind (const char *host, +#ifdef WIN32 + gboolean use_ssl, +#endif + const char *user_dn, + const char *password) +{ + LDAP *ld; + int res; + int desired_version = LDAP_VERSION3; + +#ifndef WIN32 + res = ldap_initialize (&ld, host); + if (res != LDAP_SUCCESS) { + ccnet_warning ("ldap_initialize failed: %s.\n", ldap_err2string(res)); + return NULL; + } +#else + char *host_copy = g_strdup (host); + if (!use_ssl) + ld = ldap_init (host_copy, LDAP_PORT); + else + ld = ldap_sslinit (host_copy, LDAP_SSL_PORT, 1); + g_free (host_copy); + if (!ld) { + ccnet_warning ("ldap_init failed: %ul.\n", LdapGetLastError()); + return NULL; + } +#endif + + /* set the LDAP version to be 3 */ + res = ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &desired_version); + if (res != LDAP_OPT_SUCCESS) { + ccnet_warning ("ldap_set_option failed: %s.\n", ldap_err2string(res)); + return NULL; + } + + if (user_dn) { +#ifndef WIN32 + res = ldap_bind_s (ld, user_dn, password, LDAP_AUTH_SIMPLE); +#else + char *dn_copy = g_strdup(user_dn); + char *password_copy = g_strdup(password); + res = ldap_bind_s (ld, dn_copy, password_copy, LDAP_AUTH_SIMPLE); + g_free (dn_copy); + g_free (password_copy); +#endif + if (res != LDAP_SUCCESS ) { + ccnet_warning ("ldap_bind failed for user %s: %s.\n", + user_dn, ldap_err2string(res)); + ldap_unbind_s (ld); + return NULL; + } + } + + return ld; +} + +static gboolean +get_uid_cb (CcnetDBRow *row, void *data) +{ + int *id = data; + *id = ccnet_db_row_get_column_int (row, 0); + return FALSE; +} + +static int +add_ldapuser (CcnetDB *db, + const char *email, + const char *password, + gboolean is_staff, + gboolean is_active, + const char *extra_attrs) +{ + int rc; + int uid = -1; + + rc = ccnet_db_statement_foreach_row (db, + "SELECT id FROM LDAPUsers WHERE email = ?", + get_uid_cb, &uid, 1, "string", email); + + if (rc < 0) { + return rc; + } + + if (rc == 1) { + return uid; + } + + if (extra_attrs) + rc = ccnet_db_statement_query (db, + "INSERT INTO LDAPUsers (email, password, is_staff, " + "is_active, extra_attrs) VALUES (?, ?, ?, ?, ?)", + 5, "string", email, "string", password, "int", + is_staff, "int", is_active, "string", extra_attrs); + else + rc = ccnet_db_statement_query (db, + "INSERT INTO LDAPUsers (email, password, is_staff, " + "is_active) VALUES (?, ?, ?, ?)", 4, "string", email, + "string", password, "int", is_staff, "int", is_active); + if (rc < 0) { + return rc; + } + + ccnet_db_statement_foreach_row (db, + "SELECT id FROM LDAPUsers WHERE email = ?", + get_uid_cb, &uid, 1, "string", email); + + return uid; +} + +static int ldap_verify_user_password (CcnetUserManager *manager, + const char *uid, + const char *password) +{ + LDAP *ld = NULL; + int res; + GString *filter; + char *filter_str = NULL; + char *attrs[2]; + LDAPMessage *msg = NULL, *entry; + char *dn = NULL; + int ret = 0; + + /* First search for the DN with the given uid. */ + + ld = ldap_init_and_bind (manager->ldap_host, +#ifdef WIN32 + manager->use_ssl, +#endif + manager->user_dn, + manager->password); + if (!ld) { + ccnet_warning ("Please check USER_DN and PASSWORD settings.\n"); + return -1; + } + + filter = g_string_new (NULL); + if (!manager->filter) + g_string_printf (filter, "(%s=%s)", manager->login_attr, uid); + else + g_string_printf (filter, "(&(%s=%s) (%s))", + manager->login_attr, uid, manager->filter); + filter_str = g_string_free (filter, FALSE); + + attrs[0] = manager->login_attr; + attrs[1] = NULL; + + char **base; + for (base = manager->base_list; *base; base++) { + res = ldap_search_s (ld, *base, LDAP_SCOPE_SUBTREE, + filter_str, attrs, 0, &msg); + if (res != LDAP_SUCCESS) { + ccnet_warning ("ldap_search user '%s=%s' failed for base %s: %s.\n", + manager->login_attr, uid, *base, ldap_err2string(res)); + ccnet_warning ("Please check BASE setting in ccnet.conf.\n"); + ret = -1; + ldap_msgfree (msg); + goto out; + } + + entry = ldap_first_entry (ld, msg); + if (entry) { + dn = ldap_get_dn (ld, entry); + ldap_msgfree (msg); + break; + } + + ldap_msgfree (msg); + } + + if (!dn) { + ccnet_warning ("Can't find user %s in LDAP.\n", uid); + ret = -1; + goto out; + } + + /* Then bind the DN with password. */ + + ldap_unbind_s (ld); + + ld = ldap_init_and_bind (manager->ldap_host, +#ifdef WIN32 + manager->use_ssl, +#endif + dn, password); + if (!ld) { + ccnet_warning ("Password check for %s failed.\n", uid); + ret = -1; + } + +out: + ldap_memfree (dn); + g_free (filter_str); + if (ld) ldap_unbind_s (ld); + return ret; +} + +/* + * @uid: user's uid, list all users if * is passed in. + */ +static GList *ldap_list_users (CcnetUserManager *manager, const char *uid, + int start, int limit) +{ + LDAP *ld = NULL; + GList *ret = NULL; + int res; + GString *filter; + char *filter_str; + char *attrs[2]; + LDAPMessage *msg = NULL, *entry; + + ld = ldap_init_and_bind (manager->ldap_host, +#ifdef WIN32 + manager->use_ssl, +#endif + manager->user_dn, + manager->password); + if (!ld) { + ccnet_warning ("Please check USER_DN and PASSWORD settings.\n"); + return NULL; + } + + filter = g_string_new (NULL); + if (!manager->filter) + g_string_printf (filter, "(%s=%s)", manager->login_attr, uid); + else + g_string_printf (filter, "(&(%s=%s) (%s))", + manager->login_attr, uid, manager->filter); + filter_str = g_string_free (filter, FALSE); + + attrs[0] = manager->login_attr; + attrs[1] = NULL; + + int i = 0; + if (start == -1) + start = 0; + + char **base; + for (base = manager->base_list; *base; ++base) { + res = ldap_search_s (ld, *base, LDAP_SCOPE_SUBTREE, + filter_str, attrs, 0, &msg); + if (res != LDAP_SUCCESS) { + ccnet_warning ("ldap_search user '%s=%s' failed for base %s: %s.\n", + manager->login_attr, uid, *base, ldap_err2string(res)); + ccnet_warning ("Please check BASE setting in ccnet.conf.\n"); + ret = NULL; + ldap_msgfree (msg); + goto out; + } + + for (entry = ldap_first_entry (ld, msg); + entry != NULL; + entry = ldap_next_entry (ld, entry), ++i) { + char *attr; + char **vals; + BerElement *ber; + CcnetEmailUser *user; + + if (i < start) + continue; + if (limit >= 0 && i >= start + limit) { + ldap_msgfree (msg); + goto out; + } + + attr = ldap_first_attribute (ld, entry, &ber); + vals = ldap_get_values (ld, entry, attr); + + char *email_l = g_ascii_strdown (vals[0], -1); + user = g_object_new (CCNET_TYPE_EMAIL_USER, + "id", 0, + "email", email_l, + "is_staff", FALSE, + "is_active", TRUE, + "ctime", (gint64)0, + "source", "LDAP", + "password", "!", + NULL); + g_free (email_l); + ret = g_list_prepend (ret, user); + + ldap_memfree (attr); + ldap_value_free (vals); + ber_free (ber, 0); + } + + ldap_msgfree (msg); + } + +out: + g_free (filter_str); + if (ld) ldap_unbind_s (ld); + return ret; +} + +#endif /* HAVE_LDAP */ + +/* -------- DB Operations -------- */ + +static int check_db_table (CcnetDB *db) +{ + char *sql; + + int db_type = ccnet_db_type (db); + if (db_type == CCNET_DB_TYPE_MYSQL) { + sql = "CREATE TABLE IF NOT EXISTS EmailUser (" + "id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, " + "email VARCHAR(255), passwd VARCHAR(256), " + "is_staff BOOL NOT NULL, is_active BOOL NOT NULL, " + "ctime BIGINT, UNIQUE INDEX (email))" + "ENGINE=INNODB"; + if (ccnet_db_query (db, sql) < 0) + return -1; + sql = "CREATE TABLE IF NOT EXISTS Binding (email VARCHAR(255), peer_id CHAR(41)," + "UNIQUE INDEX (peer_id), INDEX (email(20)))" + "ENGINE=INNODB"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS UserRole (" + "id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, " + "email VARCHAR(255), role VARCHAR(255), UNIQUE INDEX (email)) " + "ENGINE=INNODB"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS LDAPUsers (" + "id INTEGER PRIMARY KEY AUTO_INCREMENT, " + "email VARCHAR(255) NOT NULL, password varchar(255) NOT NULL, " + "is_staff BOOL NOT NULL, is_active BOOL NOT NULL, extra_attrs TEXT, " + "UNIQUE INDEX(email)) ENGINE=INNODB"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + } else if (db_type == CCNET_DB_TYPE_SQLITE) { + sql = "CREATE TABLE IF NOT EXISTS EmailUser (" + "id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," + "email TEXT, passwd TEXT, is_staff bool NOT NULL, " + "is_active bool NOT NULL, ctime INTEGER)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE UNIQUE INDEX IF NOT EXISTS email_index on EmailUser (email)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS Binding (email TEXT, peer_id TEXT)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE INDEX IF NOT EXISTS email_index on Binding (email)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE UNIQUE INDEX IF NOT EXISTS peer_index on Binding (peer_id)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS UserRole (email TEXT, role TEXT)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE INDEX IF NOT EXISTS userrole_email_index on UserRole (email)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE UNIQUE INDEX IF NOT EXISTS userrole_userrole_index on UserRole (email, role)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS LDAPUsers (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "email TEXT NOT NULL, password TEXT NOT NULL, " + "is_staff BOOL NOT NULL, is_active BOOL NOT NULL, extra_attrs TEXT)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE UNIQUE INDEX IF NOT EXISTS ldapusers_email_index on LDAPUsers(email)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + } else if (db_type == CCNET_DB_TYPE_PGSQL) { + sql = "CREATE TABLE IF NOT EXISTS EmailUser (" + "id SERIAL PRIMARY KEY, " + "email VARCHAR(255), passwd VARCHAR(256), " + "is_staff INTEGER NOT NULL, is_active INTEGER NOT NULL, " + "ctime BIGINT, UNIQUE (email))"; + if (ccnet_db_query (db, sql) < 0) + return -1; + sql = "CREATE TABLE IF NOT EXISTS Binding (email VARCHAR(255), peer_id CHAR(41)," + "UNIQUE (peer_id))"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS UserRole (email VARCHAR(255), " + " role VARCHAR(255), UNIQUE (email, role))"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + if (!pgsql_index_exists (db, "userrole_email_idx")) { + sql = "CREATE INDEX userrole_email_idx ON UserRole (email)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + } + + sql = "CREATE TABLE IF NOT EXISTS LDAPUsers (" + "id SERIAL PRIMARY KEY, " + "email VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL, " + "is_staff SMALLINT NOT NULL, is_active SMALLINT NOT NULL, extra_attrs TEXT)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + + if (!pgsql_index_exists (db, "ldapusers_email_idx")) { + sql = "CREATE UNIQUE INDEX ldapusers_email_idx ON LDAPUsers (email)"; + if (ccnet_db_query (db, sql) < 0) + return -1; + } + } + + return 0; +} + + +static CcnetDB * +open_sqlite_db (CcnetUserManager *manager) +{ + CcnetDB *db = NULL; + char *db_dir; + char *db_path; + + db_dir = g_build_filename (manager->session->config_dir, "PeerMgr", NULL); + if (checkdir_with_mkdir(db_dir) < 0) { + ccnet_error ("Cannot open db dir %s: %s\n", db_dir, + strerror(errno)); + return NULL; + } + g_free (db_dir); + + db_path = g_build_filename (manager->session->config_dir, "PeerMgr", + "usermgr.db", NULL); + db = ccnet_db_new_sqlite (db_path); + g_free (db_path); + + return db; +} + +static int +open_db (CcnetUserManager *manager) +{ + CcnetDB *db = NULL; + + switch (ccnet_db_type(manager->session->db)) { + /* To be compatible with the db file layout of 0.9.1 version, + * we don't use conf-dir/ccnet.db for user and peer info, but + * user conf-dir/PeerMgr/peermgr.db and conf-dir/PeerMgr/usermgr.db instead. + */ + case CCNET_DB_TYPE_SQLITE: + db = open_sqlite_db (manager); + break; + case CCNET_DB_TYPE_PGSQL: + case CCNET_DB_TYPE_MYSQL: + db = manager->session->db; + break; + } + + if (!db) + return -1; + + manager->priv->db = db; + return check_db_table (db); +} + + +/* -------- EmailUser Management -------- */ + +/* This fixed salt is used in very early versions. It's kept for compatibility. + * For the current password hashing algorithm, please see hash_password_pbkdf2_sha256() + */ +static unsigned char salt[8] = { 0xdb, 0x91, 0x45, 0xc3, 0x06, 0xc7, 0xcc, 0x26 }; + +static void +hash_password (const char *passwd, char *hashed_passwd) +{ + unsigned char sha1[20]; + SHA_CTX s; + + SHA1_Init (&s); + SHA1_Update (&s, passwd, strlen(passwd)); + SHA1_Final (sha1, &s); + rawdata_to_hex (sha1, hashed_passwd, 20); +} + +static void +hash_password_salted (const char *passwd, char *hashed_passwd) +{ + unsigned char sha[SHA256_DIGEST_LENGTH]; + SHA256_CTX s; + + SHA256_Init (&s); + SHA256_Update (&s, passwd, strlen(passwd)); + SHA256_Update (&s, salt, sizeof(salt)); + SHA256_Final (sha, &s); + rawdata_to_hex (sha, hashed_passwd, SHA256_DIGEST_LENGTH); +} + +static void +hash_password_pbkdf2_sha256 (const char *passwd, + int iterations, + char **db_passwd) +{ + guint8 sha[SHA256_DIGEST_LENGTH]; + guint8 salt[SHA256_DIGEST_LENGTH]; + char hashed_passwd[SHA256_DIGEST_LENGTH*2+1]; + char salt_str[SHA256_DIGEST_LENGTH*2+1]; + + if (!RAND_bytes (salt, sizeof(salt))) { + ccnet_warning ("Failed to generate salt " + "with RAND_bytes(), use RAND_pseudo_bytes().\n"); + RAND_pseudo_bytes (salt, sizeof(salt)); + } + + PKCS5_PBKDF2_HMAC (passwd, strlen(passwd), + salt, sizeof(salt), + iterations, + EVP_sha256(), + sizeof(sha), sha); + + rawdata_to_hex (sha, hashed_passwd, SHA256_DIGEST_LENGTH); + + rawdata_to_hex (salt, salt_str, SHA256_DIGEST_LENGTH); + + /* Encode password hash related information into one string, similar to Django. */ + GString *buf = g_string_new (NULL); + g_string_printf (buf, "PBKDF2SHA256$%d$%s$%s", + iterations, salt_str, hashed_passwd); + *db_passwd = g_string_free (buf, FALSE); +} + +static gboolean +validate_passwd_pbkdf2_sha256 (const char *passwd, const char *db_passwd) +{ + char **tokens; + char *salt_str, *hash; + int iter; + guint8 sha[SHA256_DIGEST_LENGTH]; + guint8 salt[SHA256_DIGEST_LENGTH]; + char hashed_passwd[SHA256_DIGEST_LENGTH*2+1]; + + tokens = g_strsplit (db_passwd, "$", -1); + if (!tokens || g_strv_length (tokens) != 4) { + ccnet_warning ("Invalide db passwd format %s.\n", db_passwd); + return FALSE; + } + + iter = atoi (tokens[1]); + salt_str = tokens[2]; + hash = tokens[3]; + + hex_to_rawdata (salt_str, salt, SHA256_DIGEST_LENGTH); + + PKCS5_PBKDF2_HMAC (passwd, strlen(passwd), + salt, sizeof(salt), + iter, + EVP_sha256(), + sizeof(sha), sha); + rawdata_to_hex (sha, hashed_passwd, SHA256_DIGEST_LENGTH); + + gboolean ret = (strcmp (hash, hashed_passwd) == 0); + + g_strfreev (tokens); + return ret; +} + +static gboolean +validate_passwd (const char *passwd, const char *stored_passwd, + gboolean *need_upgrade) +{ + char hashed_passwd[SHA256_DIGEST_LENGTH * 2 + 1]; + int hash_len = strlen(stored_passwd); + + *need_upgrade = FALSE; + + if (hash_len == SHA256_DIGEST_LENGTH * 2) { + hash_password_salted (passwd, hashed_passwd); + *need_upgrade = TRUE; + } else if (hash_len == SHA_DIGEST_LENGTH * 2) { + hash_password (passwd, hashed_passwd); + *need_upgrade = TRUE; + } else { + return validate_passwd_pbkdf2_sha256 (passwd, stored_passwd); + } + + if (strcmp (hashed_passwd, stored_passwd) == 0) + return TRUE; + else + return FALSE; +} + +static int +update_user_passwd (CcnetUserManager *manager, + const char *email, const char *passwd) +{ + CcnetDB *db = manager->priv->db; + char *db_passwd = NULL; + int ret; + + hash_password_pbkdf2_sha256 (passwd, manager->passwd_hash_iter, + &db_passwd); + + /* convert email to lower case for case insensitive lookup. */ + char *email_down = g_ascii_strdown (email, strlen(email)); + + ret = ccnet_db_statement_query (db, + "UPDATE EmailUser SET passwd=? WHERE email=?", + 2, "string", db_passwd, "string", email_down); + + g_free (db_passwd); + g_free (email_down); + + if (ret < 0) + return ret; + + return 0; +} + +int +ccnet_user_manager_add_emailuser (CcnetUserManager *manager, + const char *email, + const char *passwd, + int is_staff, int is_active) +{ + CcnetDB *db = manager->priv->db; + gint64 now = get_current_time(); + char *db_passwd = NULL; + int ret; + + if (!check_user_number (manager, FALSE)) { + return -1; + } + + /* A user with unhashed "!" as password cannot be logged in. + * Such users are created for book keeping, such as users from + * Shibboleth. + */ + if (g_strcmp0 (passwd, "!") != 0) + hash_password_pbkdf2_sha256 (passwd, manager->passwd_hash_iter, + &db_passwd); + else + db_passwd = g_strdup(passwd); + + /* convert email to lower case for case insensitive lookup. */ + char *email_down = g_ascii_strdown (email, strlen(email)); + + ret = ccnet_db_statement_query (db, + "INSERT INTO EmailUser(email, passwd, is_staff, " + "is_active, ctime) VALUES (?, ?, ?, ?, ?)", + 5, "string", email_down, "string", db_passwd, + "int", is_staff, "int", is_active, "int64", now); + + g_free (db_passwd); + g_free (email_down); + + if (ret < 0) + return ret; + + return 0; +} + +int +ccnet_user_manager_remove_emailuser (CcnetUserManager *manager, + const char *source, + const char *email) +{ + CcnetDB *db = manager->priv->db; + int ret; + + ccnet_db_statement_query (db, + "DELETE FROM UserRole WHERE email=?", + 1, "string", email); + + if (strcmp (source, "DB") == 0) { + ret = ccnet_db_statement_query (db, + "DELETE FROM EmailUser WHERE email=?", + 1, "string", email); + return ret; + } + +#ifdef HAVE_LDAP + if (strcmp (source, "LDAP") == 0 && manager->use_ldap) { + ret = ccnet_db_statement_query (db, + "DELETE FROM LDAPUsers WHERE email=?", + 1, "string", email); + return ret; + } +#endif + + return -1; +} + +static gboolean +get_password (CcnetDBRow *row, void *data) +{ + char **p_passwd = data; + + *p_passwd = g_strdup(ccnet_db_row_get_column_text (row, 0)); + return FALSE; +} + +int +ccnet_user_manager_validate_emailuser (CcnetUserManager *manager, + const char *email, + const char *passwd) +{ + CcnetDB *db = manager->priv->db; + char *sql; + char *email_down; + char *stored_passwd = NULL; + gboolean need_upgrade = FALSE; + + /* Users with password "!" are for internal book keeping only. */ + if (g_strcmp0 (passwd, "!") == 0) + return -1; + +#ifdef HAVE_LDAP + if (manager->use_ldap) { + if (ldap_verify_user_password (manager, email, passwd) == 0) + return 0; + } +#endif + + sql = "SELECT passwd FROM EmailUser WHERE email=?"; + if (ccnet_db_statement_foreach_row (db, sql, + get_password, &stored_passwd, + 1, "string", email) > 0) { + if (validate_passwd (passwd, stored_passwd, &need_upgrade)) { + if (need_upgrade) + update_user_passwd (manager, email, passwd); + g_free (stored_passwd); + return 0; + } else { + g_free (stored_passwd); + return -1; + } + } + + email_down = g_ascii_strdown (email, strlen(email)); + if (ccnet_db_statement_foreach_row (db, sql, + get_password, &stored_passwd, + 1, "string", email_down) > 0) { + g_free (email_down); + if (validate_passwd (passwd, stored_passwd, &need_upgrade)) { + if (need_upgrade) + update_user_passwd (manager, email, passwd); + g_free (stored_passwd); + return 0; + } else { + g_free (stored_passwd); + return -1; + } + } + g_free (email_down); + + return -1; +} + +static gboolean +get_emailuser_cb (CcnetDBRow *row, void *data) +{ + CcnetEmailUser **p_emailuser = data; + + int id = ccnet_db_row_get_column_int (row, 0); + const char *email = (const char *)ccnet_db_row_get_column_text (row, 1); + int is_staff = ccnet_db_row_get_column_int (row, 2); + int is_active = ccnet_db_row_get_column_int (row, 3); + gint64 ctime = ccnet_db_row_get_column_int64 (row, 4); + const char *password = ccnet_db_row_get_column_text (row, 5); + + char *email_l = g_ascii_strdown (email, -1); + *p_emailuser = g_object_new (CCNET_TYPE_EMAIL_USER, + "id", id, + "email", email_l, + "is_staff", is_staff, + "is_active", is_active, + "ctime", ctime, + "source", "DB", + "password", password, + NULL); + g_free (email_l); + + return FALSE; +} + +static char* +ccnet_user_manager_get_role_emailuser (CcnetUserManager *manager, + const char* email); + +static gboolean +get_ldap_emailuser_cb (CcnetDBRow *row, void *data) +{ + CcnetEmailUser **p_emailuser = data; + + int id = ccnet_db_row_get_column_int (row, 0); + const char *email = (const char *)ccnet_db_row_get_column_text (row, 1); + int is_staff = ccnet_db_row_get_column_int (row, 2); + int is_active = ccnet_db_row_get_column_int (row, 3); + + *p_emailuser = g_object_new (CCNET_TYPE_EMAIL_USER, + "id", id, + "email", email, + "is_staff", is_staff, + "is_active", is_active, + "ctime", (gint64)0, + "source", "LDAPImport", + "password", "!", + NULL); + + return FALSE; +} + +static CcnetEmailUser* +get_emailuser (CcnetUserManager *manager, + const char *email, + gboolean import) +{ + CcnetDB *db = manager->priv->db; + char *sql; + CcnetEmailUser *emailuser = NULL; + char *email_down; + + sql = "SELECT id, email, is_staff, is_active, ctime, passwd" + " FROM EmailUser WHERE email=?"; + if (ccnet_db_statement_foreach_row (db, sql, get_emailuser_cb, &emailuser, + 1, "string", email) > 0) { + char *role = ccnet_user_manager_get_role_emailuser (manager, email); + if (role) { + g_object_set (emailuser, "role", role, NULL); + g_free (role); + } + return emailuser; + } + + email_down = g_ascii_strdown (email, strlen(email)); + if (ccnet_db_statement_foreach_row (db, sql, get_emailuser_cb, &emailuser, + 1, "string", email_down) > 0) { + char *role = ccnet_user_manager_get_role_emailuser(manager, email_down); + if (role) { + g_object_set (emailuser, "role", role, NULL); + g_free (role); + } + g_free (email_down); + return emailuser; + } + +#ifdef HAVE_LDAP + if (manager->use_ldap) { + int ret = ccnet_db_statement_foreach_row (db, + "SELECT id, email, is_staff, is_active " + "FROM LDAPUsers WHERE email = ?", + get_ldap_emailuser_cb, + &emailuser, 1, "string", email_down); + if (ret < 0) { + ccnet_warning ("get ldapuser from db failed.\n"); + g_free (email_down); + return NULL; + } + + if (!emailuser) { + GList *users, *ptr; + + users = ldap_list_users (manager, email, -1, -1); + if (!users) { + g_free (email_down); + return NULL; + } + emailuser = users->data; + + /* Free all except the first user. */ + for (ptr = users->next; ptr; ptr = ptr->next) + g_object_unref (ptr->data); + g_list_free (users); + + if (import) { + if (!check_user_number (manager, FALSE)) { + g_free (email_down); + g_object_unref (emailuser); + return NULL; + } + + // add user to LDAPUsers + ret = add_ldapuser (manager->priv->db, email_down, "", + FALSE, TRUE, NULL); + if (ret < 0) { + ccnet_warning ("add ldapuser to db failed.\n"); + g_free (email_down); + g_object_unref (emailuser); + return NULL; + } + + g_object_set (emailuser, "id", ret, NULL); + } + } + + char *role = ccnet_user_manager_get_role_emailuser(manager, email_down); + if (role) { + g_object_set (emailuser, "role", role, NULL); + g_free (role); + } + g_free (email_down); + return emailuser; + } +#endif + + g_free (email_down); + + return NULL; + +} + +CcnetEmailUser* +ccnet_user_manager_get_emailuser (CcnetUserManager *manager, + const char *email) +{ + return get_emailuser (manager, email, FALSE); +} + +CcnetEmailUser* +ccnet_user_manager_get_emailuser_with_import (CcnetUserManager *manager, + const char *email) +{ + return get_emailuser (manager, email, TRUE); +} + +CcnetEmailUser* +ccnet_user_manager_get_emailuser_by_id (CcnetUserManager *manager, int id) +{ + CcnetDB *db = manager->priv->db; + char *sql; + CcnetEmailUser *emailuser = NULL; + + sql = "SELECT id, email, is_staff, is_active, ctime, passwd" + " FROM EmailUser WHERE id=?"; + if (ccnet_db_statement_foreach_row (db, sql, get_emailuser_cb, &emailuser, + 1, "int", id) < 0) + return NULL; + + return emailuser; +} + +static gboolean +get_emailusers_cb (CcnetDBRow *row, void *data) +{ + GList **plist = data; + CcnetEmailUser *emailuser; + + int id = ccnet_db_row_get_column_int (row, 0); + const char *email = (const char *)ccnet_db_row_get_column_text (row, 1); + int is_staff = ccnet_db_row_get_column_int (row, 2); + int is_active = ccnet_db_row_get_column_int (row, 3); + gint64 ctime = ccnet_db_row_get_column_int64 (row, 4); + const char *role = (const char *)ccnet_db_row_get_column_text (row, 5); + const char *password = ccnet_db_row_get_column_text (row, 6); + + char *email_l = g_ascii_strdown (email, -1); + emailuser = g_object_new (CCNET_TYPE_EMAIL_USER, + "id", id, + "email", email_l, + "is_staff", is_staff, + "is_active", is_active, + "ctime", ctime, + "role", role ? role : "", + "source", "DB", + "password", password, + NULL); + g_free (email_l); + + *plist = g_list_prepend (*plist, emailuser); + + return TRUE; +} + +static gboolean +get_ldap_emailusers_cb (CcnetDBRow *row, void *data) +{ + GList **plist = data; + CcnetEmailUser *emailuser = NULL; + + int id = ccnet_db_row_get_column_int (row, 0); + const char *email = (const char *)ccnet_db_row_get_column_text (row, 1); + int is_staff = ccnet_db_row_get_column_int (row, 2); + int is_active = ccnet_db_row_get_column_int (row, 3); + const char *role = ccnet_db_row_get_column_text (row, 4); + + emailuser = g_object_new (CCNET_TYPE_EMAIL_USER, + "id", id, + "email", email, + "is_staff", is_staff, + "is_active", is_active, + "ctime", (gint64)0, + "role", role ? role : "", + "source", "LDAPImport", + "password", "!", + NULL); + if (!emailuser) + return FALSE; + + *plist = g_list_prepend (*plist, emailuser); + + return TRUE; +} + +GList* +ccnet_user_manager_get_emailusers (CcnetUserManager *manager, + const char *source, + int start, int limit, + const char *status) +{ + CcnetDB *db = manager->priv->db; + const char *status_condition = ""; + char *sql = NULL; + GList *ret = NULL; + int rc; + +#ifdef HAVE_LDAP + if (manager->use_ldap) { + GList *users = NULL; + + if (g_strcmp0 (source, "LDAP") == 0) { + users = ldap_list_users (manager, "*", start, limit); + return g_list_reverse (users); + } else if (g_strcmp0 (source, "LDAPImport") == 0) { + if (start == -1 && limit == -1) { + if (g_strcmp0(status, "active") == 0) + status_condition = "WHERE t1.is_active = 1"; + else if (g_strcmp0(status, "inactive") == 0) + status_condition = "WHERE t1.is_active = 0"; + + sql = g_strdup_printf ("SELECT t1.id, t1.email, t1.is_staff, " + "t1.is_active, t2.role " + "FROM LDAPUsers t1 LEFT JOIN UserRole t2 " + "ON t1.email = t2.email %s", + status_condition); + + rc = ccnet_db_statement_foreach_row (db, + sql, + get_ldap_emailusers_cb, + &users, 0); + g_free (sql); + } else { + if (g_strcmp0(status, "active") == 0) + status_condition = "WHERE t1.is_active = 1"; + else if (g_strcmp0(status, "inactive") == 0) + status_condition = "WHERE t1.is_active = 0"; + + sql = g_strdup_printf ("SELECT t1.id, t1.email, t1.is_staff, " + "t1.is_active, t2.role " + "FROM LDAPUsers t1 LEFT JOIN UserRole t2 " + "ON t1.email = t2.email %s LIMIT ?, ?", + status_condition); + + rc = ccnet_db_statement_foreach_row (db, + sql, + get_ldap_emailusers_cb, + &users, 2, "int", start, "int", limit); + g_free (sql); + } + + if (rc < 0) { + while (users) { + g_object_unref (users->data); + users = g_list_delete_link (users, users); + } + return NULL; + } + return g_list_reverse (users); + } + } +#endif + + if (g_strcmp0 (source, "DB") != 0) + return NULL; + + if (start == -1 && limit == -1) { + if (g_strcmp0(status, "active") == 0) + status_condition = "WHERE t1.is_active = 1"; + else if (g_strcmp0(status, "inactive") == 0) + status_condition = "WHERE t1.is_active = 0"; + + sql = g_strdup_printf ("SELECT t1.id, t1.email, " + "t1.is_staff, t1.is_active, t1.ctime, " + "t2.role, t1.passwd FROM EmailUser AS t1 " + "LEFT JOIN UserRole AS t2 " + "ON t1.email = t2.email %s", + status_condition); + + rc = ccnet_db_statement_foreach_row (db, + sql, + get_emailusers_cb, &ret, + 0); + g_free (sql); + } else { + if (g_strcmp0(status, "active") == 0) + status_condition = "WHERE t1.is_active = 1"; + else if (g_strcmp0(status, "inactive") == 0) + status_condition = "WHERE t1.is_active = 0"; + + sql = g_strdup_printf ("SELECT t1.id, t1.email, " + "t1.is_staff, t1.is_active, t1.ctime, " + "t2.role, t1.passwd FROM EmailUser AS t1 " + "LEFT JOIN UserRole AS t2 " + "ON t1.email = t2.email %s " + "ORDER BY t1.id LIMIT ? OFFSET ?", + status_condition); + + rc = ccnet_db_statement_foreach_row (db, + sql, + get_emailusers_cb, &ret, + 2, "int", limit, "int", start); + g_free (sql); + } + + if (rc < 0) { + while (ret != NULL) { + g_object_unref (ret->data); + ret = g_list_delete_link (ret, ret); + } + return NULL; + } + + return g_list_reverse (ret); +} + +GList* +ccnet_user_manager_search_emailusers (CcnetUserManager *manager, + const char *source, + const char *keyword, + int start, int limit) +{ + CcnetDB *db = manager->priv->db; + GList *ret = NULL; + int rc; + char *db_patt = g_strdup_printf ("%%%s%%", keyword); + +#ifdef HAVE_LDAP + if (manager->use_ldap) { + if (strcmp (source, "LDAP") == 0) { + if (start == -1 && limit == -1) { + rc = ccnet_db_statement_foreach_row (db, + "SELECT t1.id, t1.email, t1.is_staff, " + "t1.is_active, t2.role " + "FROM LDAPUsers t1 LEFT JOIN UserRole t2 " + "ON t1.email = t2.email WHERE t1.email LIKE ?", + get_ldap_emailusers_cb, + &ret, 1, "string", db_patt); + } else { + rc = ccnet_db_statement_foreach_row (db, + "SELECT t1.id, t1.email, t1.is_staff, " + "t1.is_active, t2.role " + "FROM LDAPUsers t1 LEFT JOIN UserRole t2 " + "ON t1.email = t2.email WHERE t1.email LIKE ? " + "LIMIT ?, ?", + get_ldap_emailusers_cb, + &ret, 3, "string", db_patt, + "int", start, "int", limit); + } + + g_free (db_patt); + + if (rc < 0) { + while (ret) { + g_object_unref (ret->data); + ret = g_list_delete_link (ret, ret); + } + return NULL; + } + return g_list_reverse (ret); + } + } +#endif + + if (strcmp (source, "DB") != 0) { + g_free (db_patt); + return NULL; + } + + if (start == -1 && limit == -1) + rc = ccnet_db_statement_foreach_row (db, + "SELECT t1.id, t1.email, " + "t1.is_staff, t1.is_active, t1.ctime, " + "t2.role, t1.passwd FROM EmailUser AS t1 " + "LEFT JOIN UserRole AS t2 " + "ON t1.email = t2.email " + "WHERE t1.Email LIKE ? " + "ORDER BY t1.id", + get_emailusers_cb, &ret, + 1, "string", db_patt); + else + rc = ccnet_db_statement_foreach_row (db, + "SELECT t1.id, t1.email, " + "t1.is_staff, t1.is_active, t1.ctime, " + "t2.role, t1.passwd FROM EmailUser AS t1 " + "LEFT JOIN UserRole AS t2 " + "ON t1.email = t2.email " + "WHERE t1.Email LIKE ? " + "ORDER BY t1.id LIMIT ? OFFSET ?", + get_emailusers_cb, &ret, + 3, "string", db_patt, + "int", limit, "int", start); + g_free (db_patt); + if (rc < 0) { + while (ret != NULL) { + g_object_unref (ret->data); + ret = g_list_delete_link (ret, ret); + } + return NULL; + } + + return g_list_reverse (ret); +} + +GList* +ccnet_user_manager_search_ldapusers (CcnetUserManager *manager, + const char *keyword, + int start, int limit) +{ + GList *ret = NULL; + +#ifdef HAVE_LDAP + if (!manager->use_ldap) { + return NULL; + } + + char *ldap_patt = g_strdup_printf ("*%s*", keyword); + + ret = ldap_list_users (manager, ldap_patt, start, limit); + + g_free (ldap_patt); +#endif + + return ret; +} + +gint64 +ccnet_user_manager_count_emailusers (CcnetUserManager *manager, const char *source) +{ + CcnetDB* db = manager->priv->db; + char sql[512]; + gint64 ret; + +#ifdef HAVE_LDAP + if (manager->use_ldap && g_strcmp0(source, "LDAP") == 0) { + gint64 ret = ccnet_db_get_int64 (db, "SELECT COUNT(id) FROM LDAPUsers WHERE is_active = 1"); + if (ret < 0) + return -1; + return ret; + } +#endif + + if (g_strcmp0 (source, "DB") != 0) + return -1; + + snprintf (sql, 512, "SELECT COUNT(id) FROM EmailUser WHERE is_active = 1"); + + ret = ccnet_db_get_int64 (db, sql); + if (ret < 0) + return -1; + return ret; +} + +gint64 +ccnet_user_manager_count_inactive_emailusers (CcnetUserManager *manager, const char *source) +{ + CcnetDB* db = manager->priv->db; + char sql[512]; + gint64 ret; + +#ifdef HAVE_LDAP + if (manager->use_ldap && g_strcmp0(source, "LDAP") == 0) { + gint64 ret = ccnet_db_get_int64 (db, "SELECT COUNT(id) FROM LDAPUsers WHERE is_active = 0"); + if (ret < 0) + return -1; + return ret; + } +#endif + + if (g_strcmp0 (source, "DB") != 0) + return -1; + + snprintf (sql, 512, "SELECT COUNT(id) FROM EmailUser WHERE is_active = 0"); + + ret = ccnet_db_get_int64 (db, sql); + if (ret < 0) + return -1; + return ret; +} + +#if 0 +GList* +ccnet_user_manager_filter_emailusers_by_emails(CcnetUserManager *manager, + const char *emails) +{ + CcnetDB *db = manager->priv->db; + char *copy = g_strdup (emails), *saveptr; + GList *ret = NULL; + +#ifdef HAVE_LDAP + if (manager->use_ldap) + return NULL; /* todo */ +#endif + + GString *sql = g_string_new(NULL); + + g_string_append (sql, "SELECT * FROM EmailUser WHERE Email IN ("); + char *name = strtok_r (copy, ", ", &saveptr); + while (name != NULL) { + g_string_append_printf (sql, "'%s',", name); + name = strtok_r (NULL, ", ", &saveptr); + } + g_string_erase (sql, sql->len-1, 1); /* remove last "," */ + g_string_append (sql, ")"); + + if (ccnet_db_foreach_selected_row (db, sql->str, get_emailusers_cb, + &ret) < 0) { + while (ret != NULL) { + g_object_unref (ret->data); + ret = g_list_delete_link (ret, ret); + } + return NULL; + } + + g_free (copy); + g_string_free (sql, TRUE); + + return g_list_reverse (ret); +} +#endif + +int +ccnet_user_manager_update_emailuser (CcnetUserManager *manager, + const char *source, + int id, const char* passwd, + int is_staff, int is_active) +{ + CcnetDB* db = manager->priv->db; + char *db_passwd = NULL; + + // in case set user user1 to inactive, then add another active user user2, + // if current user num already the max user num, + // then reset user1 to active should fail + if (is_active && !check_user_number (manager, FALSE)) { + return -1; + } + + if (strcmp (source, "DB") == 0) { + if (g_strcmp0 (passwd, "!") == 0) { + /* Don't update passwd if it starts with '!' */ + return ccnet_db_statement_query (db, "UPDATE EmailUser SET is_staff=?, " + "is_active=? WHERE id=?", + 3, "int", is_staff, "int", is_active, + "int", id); + } else { + hash_password_pbkdf2_sha256 (passwd, manager->passwd_hash_iter, &db_passwd); + + return ccnet_db_statement_query (db, "UPDATE EmailUser SET passwd=?, " + "is_staff=?, is_active=? WHERE id=?", + 4, "string", db_passwd, "int", is_staff, + "int", is_active, "int", id); + } + } + +#ifdef HAVE_LDAP + if (manager->use_ldap && strcmp (source, "LDAP") == 0) { + return ccnet_db_statement_query (db, "UPDATE LDAPUsers SET is_staff=?, " + "is_active=? WHERE id=?", + 3, "int", is_staff, "int", is_active, + "int", id); + } +#endif + + return -1; +} + +static gboolean +get_role_emailuser_cb (CcnetDBRow *row, void *data) +{ + *((char **)data) = g_strdup (ccnet_db_row_get_column_text (row, 0)); + + return FALSE; +} + +static char* +ccnet_user_manager_get_role_emailuser (CcnetUserManager *manager, + const char* email) +{ + + CcnetDB *db = manager->priv->db; + const char *sql; + char* role; + + sql = "SELECT role FROM UserRole WHERE email=?"; + if (ccnet_db_statement_foreach_row (db, sql, get_role_emailuser_cb, &role, + 1, "string", email) > 0) + return role; + + return NULL; +} + +int +ccnet_user_manager_update_role_emailuser (CcnetUserManager *manager, + const char* email, const char* role) +{ + CcnetDB* db = manager->priv->db; + char *old_role = ccnet_user_manager_get_role_emailuser (manager, email); + if (old_role) { + g_free (old_role); + return ccnet_db_statement_query (db, "UPDATE UserRole SET role=? " + "WHERE email=?", + 2, "string", role, "string", email); + } else + return ccnet_db_statement_query (db, "INSERT INTO UserRole(role, email)" + " VALUES (?, ?)", + 2, "string", role, "string", email); +} + +GList* +ccnet_user_manager_get_superusers(CcnetUserManager *manager) +{ + CcnetDB* db = manager->priv->db; + GList *ret = NULL; + char sql[512]; + + snprintf (sql, 512, + "SELECT t1.id, t1.email, " + "t1.is_staff, t1.is_active, t1.ctime, " + "t2.role, t1.passwd FROM EmailUser AS t1 " + "LEFT JOIN UserRole AS t2 " + "ON t1.email = t2.email " + "WHERE is_staff = 1;"); + + if (ccnet_db_foreach_selected_row (db, sql, get_emailusers_cb, &ret) < 0) { + while (ret != NULL) { + g_object_unref (ret->data); + ret = g_list_delete_link (ret, ret); + } + return NULL; + } + + if (ccnet_db_foreach_selected_row (db, + "SELECT t1.id, t1.email, " + "t1.is_staff, t1.is_active, " + "t2.role FROM LDAPUsers AS t1 " + "LEFT JOIN UserRole AS t2 " + "ON t1.email = t2.email " + "WHERE is_staff = 1", + get_ldap_emailusers_cb, &ret) < 0) { + while (ret != NULL) { + g_object_unref (ret->data); + ret = g_list_delete_link (ret, ret); + } + return NULL; + } + + return g_list_reverse (ret); +} diff --git a/net/server/user-mgr.h b/net/server/user-mgr.h new file mode 100644 index 0000000..d6b8e1d --- /dev/null +++ b/net/server/user-mgr.h @@ -0,0 +1,157 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#ifndef CCNET_USER_MGR_H +#define CCNET_USER_MGR_H + +#include +#include + +#define CCNET_TYPE_USER_MANAGER (ccnet_user_manager_get_type ()) +#define CCNET_USER_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CCNET_TYPE_USER_MANAGER, CcnetUserManager)) +#define CCNET_IS_USER_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CCNET_TYPE_USER_MANAGER)) +#define CCNET_USER_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CCNET_TYPE_USER_MANAGER, CcnetUserManagerClass)) +#define CCNET_IS_USER_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CCNET_TYPE_USER_MANAGER)) +#define CCNET_USER_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CCNET_TYPE_USER_MANAGER, CcnetUserManagerClass)) + + +typedef struct _CcnetUserManager CcnetUserManager; +typedef struct _CcnetUserManagerClass CcnetUserManagerClass; + +typedef struct CcnetUserManagerPriv CcnetUserManagerPriv; + + +struct _CcnetUserManager +{ + GObject parent_instance; + + CcnetSession *session; + + char *userdb_path; + GHashTable *user_hash; + +#ifdef HAVE_LDAP + /* LDAP related */ + gboolean use_ldap; + char *ldap_host; +#ifdef WIN32 + gboolean use_ssl; +#endif + char **base_list; /* base DN from where all users can be reached */ + char *filter; /* Additional search filter */ + char *user_dn; /* DN of the admin user */ + char *password; /* password for admin user */ + char *login_attr; /* attribute name used for login */ +#endif + + int passwd_hash_iter; + + CcnetUserManagerPriv *priv; +}; + +struct _CcnetUserManagerClass +{ + GObjectClass parent_class; +}; + +GType ccnet_user_manager_get_type (void); + +CcnetUserManager* ccnet_user_manager_new (CcnetSession *); +int ccnet_user_manager_prepare (CcnetUserManager *manager); + +void ccnet_user_manager_free (CcnetUserManager *manager); + +void ccnet_user_manager_start (CcnetUserManager *manager); + +void +ccnet_user_manager_set_max_users (CcnetUserManager *manager, gint64 max_users); + +int +ccnet_user_manager_add_emailuser (CcnetUserManager *manager, + const char *email, + const char *encry_passwd, + int is_staff, int is_active); + +int +ccnet_user_manager_remove_emailuser (CcnetUserManager *manager, + const char *source, + const char *email); + +int +ccnet_user_manager_validate_emailuser (CcnetUserManager *manager, + const char *email, + const char *passwd); + +CcnetEmailUser* +ccnet_user_manager_get_emailuser (CcnetUserManager *manager, const char *email); + +CcnetEmailUser* +ccnet_user_manager_get_emailuser_with_import (CcnetUserManager *manager, + const char *email); +CcnetEmailUser* +ccnet_user_manager_get_emailuser_by_id (CcnetUserManager *manager, int id); + +/* + * @source: "DB" or "LDAP". + * @status: "", "active", or "inactive". returns all users when this argument is "". + */ +GList* +ccnet_user_manager_get_emailusers (CcnetUserManager *manager, + const char *source, + int start, int limit, + const char *status); + +GList* +ccnet_user_manager_search_emailusers (CcnetUserManager *manager, + const char *source, + const char *keyword, + int start, int limit); + +GList* +ccnet_user_manager_search_ldapusers (CcnetUserManager *manager, + const char *keyword, + int start, int limit); + +gint64 +ccnet_user_manager_count_emailusers (CcnetUserManager *manager, const char *source); + +gint64 +ccnet_user_manager_count_inactive_emailusers (CcnetUserManager *manager, const char *source); + +GList* +ccnet_user_manager_filter_emailusers_by_emails(CcnetUserManager *manager, + const char *emails); + +int +ccnet_user_manager_update_emailuser (CcnetUserManager *manager, + const char *source, + int id, const char* passwd, + int is_staff, int is_active); + +int +ccnet_user_manager_update_role_emailuser (CcnetUserManager *manager, + const char* email, const char* role); + +GList* +ccnet_user_manager_get_superusers(CcnetUserManager *manager); + +int +ccnet_user_manager_add_binding (CcnetUserManager *manager, const char *email, + const char *peer_id); + +/* Remove all bindings to an email */ +int +ccnet_user_manager_remove_binding (CcnetUserManager *manager, const char *email); + +/* Remove one specific peer-id binding to an email */ +int +ccnet_user_manager_remove_one_binding (CcnetUserManager *manager, + const char *email, + const char *peer_id); + +char * +ccnet_user_manager_get_binding_email (CcnetUserManager *manager, const char *peer_id); + +GList * +ccnet_user_manager_get_binding_peerids (CcnetUserManager *manager, const char *email); + +#endif diff --git a/python/LICENSE.txt b/python/LICENSE.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/python/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/python/Makefile.am b/python/Makefile.am new file mode 100644 index 0000000..4ee626b --- /dev/null +++ b/python/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = ccnet \ No newline at end of file diff --git a/python/ccnet/Makefile.am b/python/ccnet/Makefile.am new file mode 100644 index 0000000..1098f21 --- /dev/null +++ b/python/ccnet/Makefile.am @@ -0,0 +1,13 @@ +ccnetdir=${pyexecdir}/ccnet + +ccnet_PYTHON = __init__.py errors.py status_code.py utils.py \ + packet.py message.py \ + client.py sync_client.py \ + pool.py rpc.py + +ccnet_asyncdir = ${ccnetdir}/async + +ccnet_async_PYTHON = async/__init__.py \ + async/async_client.py async/processor.py \ + async/rpcserverproc.py async/sendcmdproc.py \ + async/mqclientproc.py async/timer.py diff --git a/python/ccnet/__init__.py b/python/ccnet/__init__.py new file mode 100644 index 0000000..16ed084 --- /dev/null +++ b/python/ccnet/__init__.py @@ -0,0 +1,7 @@ +from ccnet.errors import NetworkError +from ccnet.sync_client import SyncClient + +from ccnet.pool import ClientPool +from ccnet.rpc import RpcClientBase, CcnetRpcClient, CcnetThreadedRpcClient + +from ccnet.message import Message \ No newline at end of file diff --git a/python/ccnet/async/__init__.py b/python/ccnet/async/__init__.py new file mode 100644 index 0000000..4617964 --- /dev/null +++ b/python/ccnet/async/__init__.py @@ -0,0 +1,14 @@ +''' +@module: ccnet.async +@description: The async client of ccnet depends on python-libevent, +so we move it to a standalone package. + +''' +from .async_client import AsyncClient + +from .processor import Processor +from .rpcserverproc import RpcServerProc +from .sendcmdproc import SendCmdProc +from .mqclientproc import MqClientProc + +from .timer import Timer \ No newline at end of file diff --git a/python/ccnet/async/async_client.py b/python/ccnet/async/async_client.py new file mode 100644 index 0000000..280fa13 --- /dev/null +++ b/python/ccnet/async/async_client.py @@ -0,0 +1,250 @@ +import logging +import libevent + +from ccnet.client import Client, parse_update, parse_response + +from ccnet.packet import response_to_packet, parse_header, Packet +from ccnet.packet import to_response_id, to_master_id, to_slave_id, to_packet_id +from ccnet.packet import CCNET_MSG_REQUEST, CCNET_MSG_UPDATE, CCNET_MSG_RESPONSE, \ + CCNET_HEADER_LENGTH, CCNET_MAX_PACKET_LENGTH + +from ccnet.status_code import SC_PROC_DONE, SC_PROC_DEAD, SS_PROC_DEAD, \ + SC_UNKNOWN_SERVICE, SC_PROC_KEEPALIVE, SS_PROC_KEEPALIVE, SC_PERM_ERR + +from ccnet.status_code import PROC_NO_SERVICE, PROC_PERM_ERR, \ + PROC_BAD_RESP, PROC_REMOTE_DEAD + +from ccnet.errors import NetworkError + +from .processor import Processor +from .sendcmdproc import SendCmdProc +from .mqclientproc import MqClientProc + + +__all__ = [ + 'AsyncClient', +] + +def debug_print(msg): + print msg + +class AsyncClient(Client): + '''Async mode client''' + def __init__(self, config_dir, event_base, central_config_dir=None): + Client.__init__(self, config_dir, central_config_dir) + self.proc_types = {} + self.procs = {} + self.register_processors() + self._bev = None + + self._evbase = event_base + + def get_event_base(self): + return self._evbase + + def add_processor(self, proc): + self.procs[proc.id] = proc + + def remove_processor(self, proc): + if proc.id in self.procs: + del self.procs[proc.id] + + def get_proc(self, id): + return self.procs.get(id, None) + + def write_packet(self, pkt): + outbuf = self._bev.output + + outbuf.add(pkt.header.to_string()) + outbuf.add(pkt.body) + + def send_response(self, id, code, code_msg, content=''): + id = to_response_id(id) + pkt = response_to_packet(id, code, code_msg, content) + self.write_packet(pkt) + + def handle_packet(self, pkt): + ptype = pkt.header.ptype + if ptype == CCNET_MSG_REQUEST: + self.handle_request(pkt.header.id, pkt.body) + + elif ptype == CCNET_MSG_UPDATE: + code, code_msg, content = parse_update(pkt.body) + self.handle_update(pkt.header.id, code, code_msg, content) + + elif ptype == CCNET_MSG_RESPONSE: + code, code_msg, content = parse_response(pkt.body) + self.handle_response(pkt.header.id, code, code_msg, content) + + else: + logging.warning("unknown packet type %d", ptype) + + def handle_request(self, id, req): + commands = req.split() + self.create_slave_processor(to_slave_id(id), commands) + + def create_slave_processor(self, id, commands): + peer_id = self.peer_id + if commands[0] == 'remote': + if len(commands) < 3: + logging.warning("invalid request %s", commands) + return + peer_id = commands[1] + commands = commands[2:] + + proc_name = commands[0] + + if not proc_name in self.proc_types: + logging.warning("unknown processor type %s", proc_name) + return + + cls = self.proc_types[proc_name] + + proc = cls(proc_name, id, peer_id, self) + self.add_processor(proc) + proc.start(*commands[1:]) + + def create_master_processor(self, proc_name): + id = self.get_request_id() + + cls = self.proc_types.get(proc_name, None) + if cls == None: + logging.error('unknown processor type %s', proc_name) + return None + + proc = cls(proc_name, id, self.peer_id, self) + self.add_processor(proc) + return proc + + def handle_update(self, id, code, code_msg, content): + proc = self.get_proc(to_slave_id(id)) + if proc == None: + if code != SC_PROC_DEAD: + self.send_response(id, SC_PROC_DEAD, SS_PROC_DEAD) + return + + if code[0] == '5': + logging.info('shutdown processor %s(%d): %s %s\n', + proc.name, to_packet_id(proc.id), code, code_msg) + if code == SC_UNKNOWN_SERVICE: + proc.shutdown(PROC_NO_SERVICE) + elif code == SC_PERM_ERR: + proc.shutdown(PROC_PERM_ERR) + else: + proc.shutdown(PROC_BAD_RESP) + + elif code == SC_PROC_KEEPALIVE: + proc.send_response(SC_PROC_KEEPALIVE, SS_PROC_KEEPALIVE) + + elif code == SC_PROC_DEAD: + logging.info('shutdown processor %s(%d): when peer(%.8s) processor is dead\n', + proc.name, to_packet_id(proc.id), proc.peer_id) + proc.shutdown(PROC_REMOTE_DEAD) + + elif code == SC_PROC_DONE: + proc.done(True) + + else: + proc.handle_update(code, code_msg, content) + + def handle_response(self, id, code, code_msg, content): + proc = self.get_proc(to_master_id(id)) + if proc == None: + if code != SC_PROC_DEAD: + self.send_update(id, SC_PROC_DEAD, SS_PROC_DEAD) + return + + if code[0] == '5': + logging.info('shutdown processor %s(%d): %s %s\n', + proc.name, to_packet_id(proc.id), code, code_msg) + if code == SC_UNKNOWN_SERVICE: + proc.shutdown(PROC_NO_SERVICE) + elif code == SC_PERM_ERR: + proc.shutdown(PROC_PERM_ERR) + else: + proc.shutdown(PROC_BAD_RESP) + + elif code == SC_PROC_KEEPALIVE: + proc.send_update(id, SC_PROC_KEEPALIVE, SS_PROC_KEEPALIVE) + + elif code == SC_PROC_DEAD: + logging.info('shutdown processor %s(%d): when peer(%.8s) processor is dead\n', + proc.name, to_packet_id(proc.id), proc.peer_id) + proc.shutdown(PROC_REMOTE_DEAD) + + else: + proc.handle_response(code, code_msg, content) + + def register_processor(self, proc_name, proc_type): + assert Processor in proc_type.mro() + + self.proc_types[proc_name] = proc_type + + def register_processors(self): + self.register_processor("send-cmd", SendCmdProc) + self.register_processor("mq-client", MqClientProc) + + def register_service(self, service, group, proc_type, callback=None): + self.register_processor(service, proc_type) + cmd = 'register-service %s %s' % (service, group) + self.send_cmd(cmd, callback) + + def send_cmd(self, cmd, callback=None): + proc = self.create_master_processor("send-cmd") + if callback: + proc.set_callback(callback) + proc.start() + proc.send_cmd(cmd) + + def _read_cb(self, bev, cb_data): + dummy = bev, cb_data + + inbuf = self._bev.input + while (True): + raw = inbuf.copyout(CCNET_HEADER_LENGTH) + header = parse_header(raw) + if len(inbuf) < CCNET_HEADER_LENGTH + header.length: + break + + inbuf.drain(CCNET_HEADER_LENGTH) + data = inbuf.copyout(header.length) + pkt = Packet(header, data) + + self.handle_packet(pkt) + + inbuf.drain(header.length) + + if len(inbuf) < CCNET_HEADER_LENGTH: + break + + def _event_cb(self, bev, what, cb_data): + dummy = bev, cb_data + logging.warning('libevent error: what = %s' % what) + if what & libevent.BEV_EVENT_EOF or \ + what & libevent.BEV_EVENT_ERROR or \ + what & libevent.BEV_EVENT_READING or \ + what & libevent.BEV_EVENT_WRITING: + if self._bev is not None: + self._bev = None + raise NetworkError('libevent error: what = %s' % what) + + def base_loop(self): + '''Create an event base -> register socket events -> loop''' + self._bev = libevent.BufferEvent(self._evbase, + self._connfd.fileno()) + + self._bev.set_watermark(libevent.EV_READ, + CCNET_HEADER_LENGTH, # low wartermark + CCNET_MAX_PACKET_LENGTH * 2) # highmark + + self._bev.set_callbacks(self._read_cb, # read callback + None, # write callback + self._event_cb) # event callback + + self._bev.enable(libevent.EV_READ | libevent.EV_WRITE) + + self._evbase.loop() + + def main_loop(self): + self.base_loop() + diff --git a/python/ccnet/async/mqclientproc.py b/python/ccnet/async/mqclientproc.py new file mode 100644 index 0000000..22a7e21 --- /dev/null +++ b/python/ccnet/async/mqclientproc.py @@ -0,0 +1,50 @@ +import logging +from .processor import Processor +from ccnet.message import message_from_string, message_to_string + +INIT = 0 +REQUEST_SENT = 1 +READY = 2 + +SC_MSG = '300' +SC_UNSUBSCRIBE = '301' + +class MqClientProc(Processor): + def __init__(self, *args, **kwargs): + Processor.__init__(self, *args, **kwargs) + self.state = INIT + self.callback = None + + def start(self, *argv): + req = 'mq-server ' + ' '.join(argv) + self.send_request(req) + self.state = REQUEST_SENT + + def set_callback(self, cb): + self.callback = cb + + def handle_response(self, code, code_msg, content): + if self.state == REQUEST_SENT: + if code[0] != '2': + logging.warning('bad response: %s %s\n', code, code_msg) + self.done(False) + + self.state = READY + + elif self.state == READY: + if code[0] != '2' and code[0] != '3': + logging.warning('bad response: %s %s\n', code, code_msg) + return + + if code[0] == '3' and code[2] == '0': + msg = message_from_string(content[:-1]) + if self.callback: + self.callback(msg) + + def put_message(self, msg): + buf = message_to_string(msg) + self.send_update(SC_MSG, '', buf + '\000') + + def unsubscribe(self): + self.send_update(SC_UNSUBSCRIBE, '') + self.done(True) \ No newline at end of file diff --git a/python/ccnet/async/processor.py b/python/ccnet/async/processor.py new file mode 100644 index 0000000..1071162 --- /dev/null +++ b/python/ccnet/async/processor.py @@ -0,0 +1,57 @@ +import logging + +from ccnet.packet import SLAVE_BIT_MASK, to_print_id +from ccnet.status_code import SC_PROC_DONE, SS_PROC_DONE, PROC_DONE + +class Processor(object): + """Base processor class""" + + name = "Processor" + def __init__(self, name, id, peer_id, client): + self.name = name + self.id = id + self.peer_id = peer_id + self.client = client + + def start(self, *args, **kwargs): + raise NotImplementedError + + def handle_request(self, *args, **kwargs): + raise NotImplementedError + + def handle_update(self, *args, **kwargs): + raise NotImplementedError + + def handle_response(self, *args, **kwargs): + raise NotImplementedError + + def __str__(self): + return "" % (self.name, to_print_id(self.id)) + + def is_master(self): + return not (self.id & SLAVE_BIT_MASK) + + def send_request(self, buf): + assert self.is_master() + + return self.client.send_request(self.id, buf) + + def send_response(self, code, code_msg, content=''): + assert not self.is_master() + + return self.client.send_response(self.id, code, code_msg, content) + + def send_update(self, code, code_msg, content=''): + assert self.is_master() + + return self.client.send_update(self.id, code, code_msg, content) + + def done(self, success): + if self.is_master() and success: + self.send_update(SC_PROC_DONE, SS_PROC_DONE, '') + self.client.remove_processor(self) + + def shutdown(self, reason): + if reason > PROC_DONE: + logging.debug('shut down %s: %s', self, reason) + self.client.remove_processor(self) \ No newline at end of file diff --git a/python/ccnet/async/rpcserverproc.py b/python/ccnet/async/rpcserverproc.py new file mode 100644 index 0000000..6e13678 --- /dev/null +++ b/python/ccnet/async/rpcserverproc.py @@ -0,0 +1,43 @@ +from pysearpc import searpc_server + +from ccnet.status_code import SC_OK, SS_OK +from ccnet.status_code import SC_SERVER_RET, SS_SERVER_RET, SC_SERVER_MORE, SS_SERVER_MORE, \ + SC_CLIENT_CALL, SC_CLIENT_MORE, SC_CLIENT_CALL_MORE + +from .processor import Processor + +class RpcServerProc(Processor): + name = 'rpcserver-proc' + max_transfer_length = 65535 - 128 + + def __init__(self, *args, **kwargs): + Processor.__init__(self, *args, **kwargs) + self.fretstr = '' + self.fcallstr = '' + + def start(self, *argv): + self.send_response(SC_OK, SS_OK, '') + + def send_fret(self): + maxlen = self.max_transfer_length + l = len(self.fretstr) + if l < maxlen: + self.send_response(SC_SERVER_RET, SS_SERVER_RET, self.fretstr) + self.fretstr = '' + + else: + buf = self.fretstr[:maxlen] + self.send_response(SC_SERVER_MORE, SS_SERVER_MORE, buf) + self.fretstr = self.fretstr[maxlen:] + + def handle_update(self, code, code_msg, content): + if code == SC_CLIENT_CALL_MORE: + self.fcallstr += content + return + elif code == SC_CLIENT_CALL: + self.fcallstr += content + self.fretstr = searpc_server.call_function(self.name, self.fcallstr) + self.fcallstr = '' + self.send_fret() + elif code == SC_CLIENT_MORE: + self.send_fret() \ No newline at end of file diff --git a/python/ccnet/async/sendcmdproc.py b/python/ccnet/async/sendcmdproc.py new file mode 100644 index 0000000..50b7b50 --- /dev/null +++ b/python/ccnet/async/sendcmdproc.py @@ -0,0 +1,34 @@ +import logging +from .processor import Processor + +INIT = 0 +REQUET_SENT = 1 +CONNECTED = 2 + +class SendCmdProc(Processor): + name = "send-cmd" + def __init__(self, *args, **kwargs): + Processor.__init__(self, *args, **kwargs) + self.callback = None + self.state = INIT + + def start(self, *argv): + self.send_request('receive-cmd') + self.state = REQUET_SENT + + def set_callback(self, cb): + self.callback = cb + + def send_cmd(self, cmd): + self.send_update('200', '', cmd + '\000') + + def handle_response(self, code, code_msg, content): + if code[0] != '2': + logging.warning("Received bad response %s %s", code, code_msg) + + if self.state == REQUET_SENT: + self.state = CONNECTED + + elif self.state == CONNECTED: + if self.callback: + self.callback(code, code_msg, content) \ No newline at end of file diff --git a/python/ccnet/async/timer.py b/python/ccnet/async/timer.py new file mode 100644 index 0000000..fba0a9b --- /dev/null +++ b/python/ccnet/async/timer.py @@ -0,0 +1,21 @@ +import libevent +import logging + +class Timer(object): + '''Wraps aroud a libevent timeout event''' + def __init__(self, ev_base, timeout): + self._timeout = timeout + self._evtimer = libevent.Timer(ev_base, self._callback, None) + self._evtimer.add(timeout) # pylint: disable=E1101 + + def _callback(self, evtimer, user_data): + dummy = user_data + try: + self.callback() + except: + logging.exception('error in timer callback:') + + evtimer.add(self._timeout) + + def callback(self): + raise NotImplementedError \ No newline at end of file diff --git a/python/ccnet/client.py b/python/ccnet/client.py new file mode 100644 index 0000000..e4d2e4e --- /dev/null +++ b/python/ccnet/client.py @@ -0,0 +1,145 @@ +#coding: UTF-8 + +import os +import socket +import ConfigParser +import logging + +from ccnet.packet import to_request_id, to_update_id +from ccnet.packet import request_to_packet, update_to_packet +from ccnet.packet import write_packet + +from ccnet.errors import NetworkError + +from .utils import is_win32, make_socket_closeonexec + +CCNET_PIPE_NAME = 'ccnet.sock' + +def parse_response(body): + '''Parse the content of the response + The struct of response data: + - first 3 bytes is the + - from the 4th byte to the first occurrence of '\n' is the . If the 4th byte is '\n', then there is no + - from the first occurrence of '\n' to the end is the + ''' + code = body[:3] + if body[3] == '\n': + code_msg = '' + content = body[4:] + else: + pos = body.index('\n') + code_msg = body[4:pos] + content = body[pos + 1:] + + return code, code_msg, content + +def parse_update(body): + '''The structure of an update is the same with a response''' + code = body[:3] + if body[3] == '\n': + code_msg = '' + content = body[4:] + else: + pos = body.index('\n') + code_msg = body[4:pos] + content = body[pos + 1:] + + return code, code_msg, content + +class Client(object): + '''Base ccnet client class''' + def __init__(self, config_dir, central_config_dir=None): + if not isinstance(config_dir, unicode): + config_dir = config_dir.decode('UTF-8') + + if central_config_dir: + central_config_dir = os.path.expanduser(central_config_dir) + if not os.path.exists(central_config_dir): + raise RuntimeError(u'%s does not exits' % central_config_dir) + config_dir = os.path.expanduser(config_dir) + config_file = os.path.join(central_config_dir if central_config_dir else config_dir, + u'ccnet.conf') + logging.debug('using config file %s', config_file) + if not os.path.exists(config_file): + raise RuntimeError(u'%s does not exits' % config_file) + + self.central_config_dir = central_config_dir + self.config_dir = config_dir + self.config_file = config_file + self.config = None + + self.port = None + self.peer_id = None + self.peer_name = None + + self.parse_config() + + self._connfd = None + self._req_id = 1000 + + def __del__(self): + '''Destructor of the client class. We close the socket here, if + connetced to daemon + + ''' + if self.is_connected(): + try: + self._connfd.close() + except: + pass + + def parse_config(self): + self.config = ConfigParser.ConfigParser() + self.config.read(self.config_file) + self.port = self.config.getint('Client', 'PORT') + self.un_path = '' + if self.config.has_option('Client', 'UNIX_SOCKET'): + self.un_path = self.config.get('Client', 'UNIX_SOCKET') + self.peer_id = self.config.get('General', 'ID') + self.peer_name = self.config.get('General', 'NAME') + + def connect_daemon_with_pipe(self): + self._connfd = socket.socket(socket.AF_UNIX) + if not self.un_path: + pipe_name = os.path.join(self.config_dir, CCNET_PIPE_NAME) + else: + pipe_name = self.un_path + try: + self._connfd.connect(pipe_name) + except: + raise NetworkError("Can't connect to daemon") + + make_socket_closeonexec(self._connfd.fileno()) + + def connect_daemon_with_socket(self): + self._connfd = socket.socket() + self._connfd.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) + try: + self._connfd.connect(('127.0.0.1', self.port)) + except: + raise NetworkError("Can't connect to daemon") + + make_socket_closeonexec(self._connfd.fileno()) + + def connect_daemon(self): + if is_win32(): + return self.connect_daemon_with_socket() + else: + return self.connect_daemon_with_pipe() + + def is_connected(self): + return self._connfd is not None + + def send_request(self, id, req): + id = to_request_id(id) + pkt = request_to_packet(id, req) + write_packet(self._connfd, pkt) + + def send_update(self, id, code, code_msg, content=''): + id = to_update_id(id) + pkt = update_to_packet(id, code, code_msg, content) + write_packet(self._connfd, pkt) + + def get_request_id(self): + self._req_id += 1 + return self._req_id diff --git a/python/ccnet/errors.py b/python/ccnet/errors.py new file mode 100644 index 0000000..f7caa70 --- /dev/null +++ b/python/ccnet/errors.py @@ -0,0 +1,7 @@ +class NetworkError(Exception): + def __init__(self, msg): + Exception.__init__(self) + self.msg = msg + + def __str__(self): + return self.msg diff --git a/python/ccnet/message.py b/python/ccnet/message.py new file mode 100644 index 0000000..d8d867b --- /dev/null +++ b/python/ccnet/message.py @@ -0,0 +1,47 @@ +#coding: UTF-8 + +'''Message is the carrier of a simple Pub/Sub system on top of ccnet''' + +import datetime +import re +import uuid +import time + +MESSAGE_PATTERN = re.compile(r'(?P[\d]+) (?P[^ ]+) (?P[^ ]+) (?P[^ ]+) (?P[^ ]+) (?P[^ ]+) (?P[^ ]+) (?P.*)') + +class Message(object): + def __init__(self, d): + self.flags = int(d['flags']) + self.from_ = d['from'] + self.to = d['to'] + self.id = d['id'] + self.ctime = float(d['ctime']) + self.rtime = float(d['rtime']) + self.app = d['app'] + self.body = d['body'] + +def message_from_string(s): + results = MESSAGE_PATTERN.match(s) + if results is None: + raise RuntimeError('Bad message: %s' % s) + + d = results.groupdict() + return Message(d) + + +def gen_inner_message_string(self_id, app, content): + result = "%d %s %s %s %d %d %s %s\000" % (0, self_id, self_id, str(uuid.uuid1()), + int(time.time()), 0, + app, content) + return result + +def message_to_string(msg): + f = '%(flags)s %(from_)s %(to)s %(id)s %(ctime)s %(rtime)s %(app)s %(body)s' + return f % dict(flags=msg.flags, + from_=msg.from_, + to=msg.to, + id=msg.id, + ctime=msg.ctime, + rtime=msg.rtime, + app=msg.app, + body=msg.body) diff --git a/python/ccnet/packet.py b/python/ccnet/packet.py new file mode 100644 index 0000000..5780c49 --- /dev/null +++ b/python/ccnet/packet.py @@ -0,0 +1,129 @@ +#coding: UTF-8 + +""" +Packet level protocol of ccnet. + +About various types of id: + + - A slave processor's id has its highest bit set; a master processor has its highest bit clear + - The field of a ccnet packet always has its highest bit clear. The + field of the packet determines what type of the packet is (a + request, a response, or an update) +""" + +import logging +import struct + +from ccnet.utils import recvall, sendall, NetworkError + +REQUEST_ID_MASK = 0x7fffffff +SLAVE_BIT_MASK = 0x80000000 + +CCNET_MSG_OK = 0 +CCNET_MSG_HANDSHAKE = 1 +CCNET_MSG_REQUEST = 2 +CCNET_MSG_RESPONSE = 3 +CCNET_MSG_UPDATE = 4 +CCNET_MSG_RELAY = 5 + +def to_request_id(id): + return id & REQUEST_ID_MASK + +to_response_id = to_request_id +to_update_id = to_request_id +to_master_id = to_request_id +to_packet_id = to_request_id + +def to_slave_id(id): + return id | SLAVE_BIT_MASK + +def to_print_id(id): + if id & SLAVE_BIT_MASK: + return -to_request_id(id) + else: + return id + +# the byte sequence of ccnet packet header +CCNET_HEADER_FORMAT = '>BBHI' +# Number of bytes for the header +CCNET_HEADER_LENGTH = struct.calcsize(CCNET_HEADER_FORMAT) + +CCNET_MAX_PACKET_LENGTH = 65535 + +class PacketHeader(object): + def __init__(self, ver, ptype, length, id): + self.ver = ver + self.ptype = ptype + self.length = length + self.id = id + + def to_string(self): + return struct.pack(CCNET_HEADER_FORMAT, self.ver, self.ptype, self.length, self.id) + + def __str__(self): + return "" % (self.ptype, self.length, self.id) + +class Packet(object): + version = 1 + def __init__(self, header, body): + self.header = header + self.body = body + +def parse_header(buf): + try: + ver, ptype, length, id = struct.unpack(CCNET_HEADER_FORMAT, buf) + except struct.error, e: + raise NetworkError('error when unpack packet header: %s' % e) + + return PacketHeader(ver, ptype, length, id) + +def format_response(code, code_msg, content): + body = code + if code_msg: + body += " " + code_msg + body += "\n" + + if content: + body += content + + return body + +format_update = format_response + +def request_to_packet(id, buf): + hdr = PacketHeader(1, CCNET_MSG_REQUEST, len(buf), to_request_id(id)) + return Packet(hdr, buf) + +def response_to_packet(id, code, code_msg, content): + body = format_response(code, code_msg, content) + hdr = PacketHeader(1, CCNET_MSG_RESPONSE, len(body), to_response_id(id)) + return Packet(hdr, body) + +def update_to_packet(id, code, code_msg, content): + body = format_update(code, code_msg, content) + hdr = PacketHeader(1, CCNET_MSG_UPDATE, len(body), to_update_id(id)) + return Packet(hdr, body) + +def read_packet(fd): + hdr = recvall(fd, CCNET_HEADER_LENGTH) + if len(hdr) == 0: + logging.warning('connection to daemon is lost') + raise NetworkError('Connection to daemon is lost') + elif len(hdr) < CCNET_HEADER_LENGTH: + raise NetworkError('Only read %d bytes header, expected 8' % len(hdr)) + + header = parse_header(hdr) + + if header.length == 0: + body = '' + else: + body = recvall(fd, header.length) + if len(body) < header.length: + raise NetworkError('Only read %d bytes body, expected %d' % (len(body), header.length)) + + return Packet(header, body) + +def write_packet(fd, packet): + hdr = packet.header.to_string() + sendall(fd, hdr) + sendall(fd, packet.body) \ No newline at end of file diff --git a/python/ccnet/pool.py b/python/ccnet/pool.py new file mode 100644 index 0000000..e738dcb --- /dev/null +++ b/python/ccnet/pool.py @@ -0,0 +1,36 @@ +from ccnet.sync_client import SyncClient +import Queue + +class ClientPool(object): + """ccnet client pool.""" + + def __init__(self, conf_dir, pool_size=5, central_config_dir=None): + """ + :param central_config_dir: path to the central config dir for ccnet/seafile/seahub/seafdav etc. + :param conf_dir: the ccnet configuration directory + :param pool_size: + """ + self.central_config_dir = central_config_dir + self.conf_dir = conf_dir + self.pool_size = pool_size + self._pool = Queue.Queue(pool_size) + + def _create_client(self): + client = SyncClient(self.conf_dir, self.central_config_dir) + client.req_ids = {} + client.connect_daemon() + + return client + + def get_client(self): + try: + client = self._pool.get(False) + except: + client = self._create_client() + return client + + def return_client(self, client): + try: + self._pool.put(client, False) + except Queue.Full: + pass diff --git a/python/ccnet/rpc.py b/python/ccnet/rpc.py new file mode 100644 index 0000000..2357053 --- /dev/null +++ b/python/ccnet/rpc.py @@ -0,0 +1,410 @@ +from pysearpc import SearpcClient, searpc_func, SearpcError + +from ccnet.status_code import SC_CLIENT_CALL, SS_CLIENT_CALL, \ + SC_CLIENT_MORE, SS_CLIENT_MORE, SC_SERVER_RET, \ + SC_SERVER_MORE, SC_PROC_DEAD + +from ccnet.errors import NetworkError + +class DeadProcError(Exception): + def __str__(self): + return "Processor is dead" + + +class RpcClientBase(SearpcClient): + + def __init__(self, ccnet_client_pool, service_name, retry_num=1, + is_remote=False, remote_peer_id='', req_pool=False): + SearpcClient.__init__(self) + self.pool = ccnet_client_pool + self.service_name = service_name + self.retry_num = retry_num + self.is_remote = is_remote + self.remote_peer_id = remote_peer_id + self.req_pool = req_pool + if self.is_remote and len(self.remote_peer_id) != 40: + raise ValueError("Invalid remote peer id") + + def _start_service(self, client): + req_id = client.get_request_id() + req_str = self.service_name + if self.is_remote: + req_str = "remote " + self.remote_peer_id + " " + self.service_name + client.send_request(req_id, req_str) + rsp = client.read_response() + if rsp.code != "200": + raise SearpcError("Error received: %s %s (In _start_service)" % (rsp.code, rsp.code_msg)) + return req_id + + def _real_call(self, client, req_id, fcall_str): + client.send_update(req_id, SC_CLIENT_CALL, SS_CLIENT_CALL, fcall_str) + + rsp = client.read_response() + if rsp.code == SC_SERVER_RET: + return rsp.content + elif rsp.code == SC_SERVER_MORE: + buf = rsp.content + while True: + client.send_update(req_id, SC_CLIENT_MORE, + SS_CLIENT_MORE, '') + rsp = client.read_response() + if rsp.code == SC_SERVER_MORE: + buf += rsp.content + elif rsp.code == SC_SERVER_RET: + buf += rsp.content + break + else: + raise SearpcError("Error received: %s %s (In Read More)" % (rsp.code, rsp.code_msg)) + + return buf + elif rsp.code == SC_PROC_DEAD: + raise DeadProcError() + else: + raise SearpcError("Error received: %s %s" % (rsp.code, rsp.code_msg)) + + def call_remote_func_sync(self, fcall_str): + """Call remote function `fcall_str` and wait response.""" + + retried = 0 + while True: + try: + client = self.pool.get_client() + if self.req_pool: + req_id = client.req_ids.get(self.service_name, -1) + if req_id == -1: + req_id = self._start_service(client) + client.req_ids[self.service_name] = req_id + try: + ret = self._real_call(client, req_id, fcall_str) + except DeadProcError: + client.req_ids[self.service_name] = -1 + self.pool.return_client(client) + if retried < self.retry_num: + retried = retried + 1 + continue + else: + raise + + self.pool.return_client(client) + return ret + else: + # no req pool + req_id = self._start_service(client) + ret = self._real_call(client, req_id, fcall_str) + client.send_update(req_id, "103", "service is done", "") + self.pool.return_client(client) + return ret + except (NetworkError, SearpcError): + # the client is not returned to the pool and is freed automatically + if retried < self.retry_num: + retried = retried + 1 + continue + else: + raise + +class CcnetRpcClient(RpcClientBase): + + def __init__(self, ccnet_client_pool, retry_num=1, *args, **kwargs): + RpcClientBase.__init__(self, ccnet_client_pool, "ccnet-rpcserver", + *args, **kwargs) + + @searpc_func("string", []) + def list_peers(self): + pass + + @searpc_func("objlist", []) + def list_resolving_peers(self): + pass + + @searpc_func("objlist", ["string"]) + def get_peers_by_role(self): + pass + + @searpc_func("object", ["string"]) + def get_peer(self): + pass + + @searpc_func("object", []) + def get_session_info(self): + pass + + @searpc_func("int", ["string"]) + def add_client(self): + pass + + @searpc_func("int", ["string", "string"]) + def add_role(self, peer_id, role): + pass + + @searpc_func("int", ["string", "string"]) + def remove_role(self, peer_id, role): + pass + + @searpc_func("objlist", ["int", "int"]) + def get_procs_alive(self, offset, limit): + pass + + @searpc_func("int", []) + def count_procs_alive(self): + pass + + @searpc_func("objlist", ["int", "int"]) + def get_procs_dead(self, offset, limit): + pass + + @searpc_func("int", []) + def count_procs_dead(self): + pass + + @searpc_func("string", ["string"]) + def get_config(self, key): + pass + + @searpc_func("int", ["string", "string"]) + def set_config(self, key, value): + pass + + @searpc_func("objlist", []) + def list_peer_stat(self, key, value): + pass + + +class CcnetThreadedRpcClient(RpcClientBase): + + def __init__(self, ccnet_client_pool, retry_num=1, *args, **kwargs): + RpcClientBase.__init__(self, ccnet_client_pool, "ccnet-threaded-rpcserver", + *args, **kwargs) + + @searpc_func("int", ["string", "string", "int", "int"]) + def add_emailuser(self, email, passwd, is_staff, is_active): + pass + + @searpc_func("int", ["string", "string"]) + def remove_emailuser(self, source, email): + pass + + @searpc_func("int", ["string", "string"]) + def validate_emailuser(self, email, passwd): + pass + + @searpc_func("object", ["string"]) + def get_emailuser(self, email): + pass + + @searpc_func("object", ["string"]) + def get_emailuser_with_import(self, email): + pass + + @searpc_func("object", ["int"]) + def get_emailuser_by_id(self, user_id): + pass + + @searpc_func("objlist", ["string", "int", "int", "string"]) + def get_emailusers(self, source, start, limit, status): + pass + + @searpc_func("objlist", ["string", "string", "int", "int"]) + def search_emailusers(self, source, email_patt, start, limit): + pass + + @searpc_func("objlist", ["string", "int", "int"]) + def search_ldapusers(self, keyword, start, limit): + pass + + @searpc_func("int64", ["string"]) + def count_emailusers(self, source): + pass + + @searpc_func("int64", ["string"]) + def count_inactive_emailusers(self, source): + pass + + @searpc_func("objlist", ["string"]) + def filter_emailusers_by_emails(self): + pass + + @searpc_func("int", ["string", "int", "string", "int", "int"]) + def update_emailuser(self, source, user_id, password, is_staff, is_active): + pass + + @searpc_func("int", ["string", "string"]) + def update_role_emailuser(self, email, role): + pass + + @searpc_func("objlist", []) + def get_superusers(self): + pass + + @searpc_func("int", ["string", "string"]) + def add_binding(self, email, peer_id): + pass + + @searpc_func("string", ["string"]) + def get_binding_email(self, peer_id): + pass + + @searpc_func("string", ["string"]) + def get_binding_peerids(self, email): + pass + + @searpc_func("int", ["string"]) + def remove_binding(self, email): + pass + + @searpc_func("int", ["string", "string"]) + def remove_one_binding(self, email, peer_id): + pass + + @searpc_func("objlist", ["string"]) + def get_peers_by_email(self, email): + pass + + @searpc_func("int", ["string", "string", "string"]) + def create_group(self, group_name, user_name, gtype): + pass + + @searpc_func("int", ["int", "string", "string"]) + def create_org_group(self, org_id, group_name, user_name): + pass + + @searpc_func("int", ["int"]) + def remove_group(self, group_id): + pass + + @searpc_func("int", ["int", "string", "string"]) + def group_add_member(self, group_id, user_name, member_name): + pass + + @searpc_func("int", ["int", "string", "string"]) + def group_remove_member(self, group_id, user_name, member_name): + pass + + @searpc_func("int", ["int", "string"]) + def group_set_admin(self, group_id, member_name): + pass + + @searpc_func("int", ["int", "string"]) + def group_unset_admin(self, group_id, member_name): + pass + + @searpc_func("int", ["int", "string"]) + def set_group_name(self, group_id, group_name): + pass + + @searpc_func("int", ["int", "string"]) + def quit_group(self, group_id, user_name): + pass + + @searpc_func("objlist", ["string"]) + def get_groups(self, user_name): + pass + + @searpc_func("objlist", ["int", "int", "string"]) + def get_all_groups(self, start, limit, source): + pass + + @searpc_func("object", ["int"]) + def get_group(self, group_id): + pass + + @searpc_func("objlist", ["int"]) + def get_group_members(self, group_id): + pass + + @searpc_func("int", ["int", "string"]) + def check_group_staff(self, group_id, username): + pass + + @searpc_func("int", ["string"]) + def remove_group_user(self, username): + pass + + @searpc_func("int", ["int", "string"]) + def is_group_user(self, group_id, user): + pass + + @searpc_func("int", ["int", "string"]) + def set_group_creator(self, group_id, user_name): + pass + + @searpc_func("int", ["string", "string", "string"]) + def create_org(self, org_name, url_prefix, creator): + pass + + @searpc_func("int", ["int"]) + def remove_org(self, org_id): + pass + + @searpc_func("objlist", ["int", "int"]) + def get_all_orgs(self, start, limit): + pass + + @searpc_func("int64", []) + def count_orgs(self): + pass + + @searpc_func("object", ["string"]) + def get_org_by_url_prefix(self, url_prefix): + pass + + @searpc_func("object", ["string"]) + def get_org_by_id(self, org_id): + pass + + @searpc_func("int", ["int", "string", "int"]) + def add_org_user(self, org_id, email, is_staff): + pass + + @searpc_func("int", ["int", "string"]) + def remove_org_user(self, org_id, email): + pass + + @searpc_func("objlist", ["string"]) + def get_orgs_by_user(self, email): + pass + + @searpc_func("objlist", ["string", "int", "int"]) + def get_org_emailusers(self, url_prefix, start, limit): + pass + + @searpc_func("int", ["int", "int"]) + def add_org_group(self, org_id, group_id): + pass + + @searpc_func("int", ["int", "int"]) + def remove_org_group(self, org_id, group_id): + pass + + @searpc_func("int", ["int"]) + def is_org_group(self, group_id): + pass + + @searpc_func("int", ["int"]) + def get_org_id_by_group(self, group_id): + pass + + @searpc_func("objlist", ["int", "int", "int"]) + def get_org_groups(self, org_id, start, limit): + pass + + @searpc_func("int", ["int", "string"]) + def org_user_exists(self, org_id, email): + pass + + @searpc_func("int", ["int", "string"]) + def is_org_staff(self, org_id, user): + pass + + @searpc_func("int", ["int", "string"]) + def set_org_staff(self, org_id, user): + pass + + @searpc_func("int", ["int", "string"]) + def unset_org_staff(self, org_id, user): + pass + + @searpc_func("int", ["int", "string"]) + def set_org_name(self, org_id, org_name): + pass + + diff --git a/python/ccnet/status_code.py b/python/ccnet/status_code.py new file mode 100644 index 0000000..4241074 --- /dev/null +++ b/python/ccnet/status_code.py @@ -0,0 +1,77 @@ +#coding: UTF-8 + +'''Status code and status messages used in ccnet. Should be treated as constants''' + +EC_NETWORK_ERR = 1 +ES_NETWORK_ERR = 'Network Error' + +SC_PROC_KEEPALIVE = '100' +SS_PROC_KEEPALIVE = 'processor keep alive' +SC_PROC_ALIVE = '101' +SS_PROC_ALIVE = 'processor is alive' +SC_PROC_DEAD = '102' +SS_PROC_DEAD = 'processor is dead' +SC_PROC_DONE = '103' +SS_PROC_DONE = 'service is done' + + +SC_OK = '200' +SS_OK = 'OK' +SC_SERV_EXISTED = '210' +SS_SERV_EXISTED = 'The service existed' +SC_PERM_CHECKING = '250' +SS_PERM_CHECKING = 'Permission Checking' + + +SC_SHUTDOWN = '500' +SS_SHUTDOWN = 'Shutdown' +SC_CREATE_PROC_ERR = '501' +SS_CREATE_PROC_ERR = 'Create Processor Error' +SC_BAD_PEER = '502' +SS_BAD_PEER = 'Bad peer id' +SC_BAD_USER = '502' +SS_BAD_USER = 'Bad user id' +SC_BAD_ARGS = '503' +SS_BAD_ARGS = 'Bad arguments' +SC_PERM_ERR = '504' +SS_PERM_ERR = 'Permission Error' +SC_BAD_UPDATE_CODE = '506' +SS_BAD_UPDATE_CODE = 'Bad update code' +SC_BAD_RESPONSE_CODE = '507' +SS_BAD_RESPONSE_CODE = 'Bad response code' +SC_VERSION_MISMATCH = '508' +SS_VERSION_MISMATCH = 'Version Mismatch' +SC_UNKNOWN_PEER = '510' +SS_UNKNOWN_PEER = 'Unknown peer' +SC_UNKNOWN_SERVICE = '511' +SS_UNKNOWN_SERVICE = 'Unknown service' +SC_PEER_UNREACHABLE = '512' +SS_PEER_UNREACHABLE = 'Peer Unreachable' +SC_CON_TIMEOUT = '513' +SS_CON_TIMEOUT = 'connection timeout' +SC_KEEPALIVE_TIMEOUT = '514' +SS_KEEPALIVE_TIMEOUT = 'keepalive timeout' +SC_NETDOWN = '515' +SS_NETDOWN = 'peer down' + + + +PROC_NOTSET = 0 +PROC_DONE = 1 +PROC_REMOTE_DEAD = 2 +PROC_NO_SERVICE = 3 +PROC_PERM_ERR = 4 +PROC_BAD_RESP = 5 + +SC_CLIENT_CALL = '301' +SS_CLIENT_CALL = 'CLIENT CALL' +SC_CLIENT_MORE = '302' +SS_CLIENT_MORE = 'MORE' +SC_CLIENT_CALL_MORE = '303' +SS_CLIENT_CALL_MORE = 'CLIENT HAS MORE' +SC_SERVER_RET = '311' +SS_SERVER_RET = 'SERVER RET' +SC_SERVER_MORE = '312' +SS_SERVER_MORE = 'HAS MORE' +SC_SERVER_ERR = '411' +SS_SERVER_ERR = 'Fail to invoke the function, check the function' \ No newline at end of file diff --git a/python/ccnet/sync_client.py b/python/ccnet/sync_client.py new file mode 100644 index 0000000..67adbd7 --- /dev/null +++ b/python/ccnet/sync_client.py @@ -0,0 +1,94 @@ +from ccnet.client import Client, parse_response +from ccnet.packet import read_packet, CCNET_MSG_RESPONSE +from ccnet.status_code import SC_PROC_DONE, SS_PROC_DONE +from ccnet.message import message_from_string, gen_inner_message_string + +_REQ_ID_START = 1000 + +class Response(object): + def __init__(self, code, code_msg, content): + self.code = code + self.code_msg = code_msg + self.content = content + +class SyncClient(Client): + '''sync mode client''' + def __init__(self, config_dir, central_config_dir=None): + Client.__init__(self, config_dir, central_config_dir) + self._req_id = _REQ_ID_START + self.mq_req_id = -1 + + def disconnect_daemon(self): + if self.is_connected(): + try: + self._connfd.close() + except: + pass + + def read_response(self): + packet = read_packet(self._connfd) + if packet.header.ptype != CCNET_MSG_RESPONSE: + raise RuntimeError('Invalid Response') + + code, code_msg, content = parse_response(packet.body) + + return Response(code, code_msg, content) + + def send_cmd(self, cmd): + req_id = self.get_request_id() + self.send_request(req_id, 'receive-cmd') + resp = self.read_response() + if resp.code != '200': + raise RuntimeError('Failed to send-cmd: %s %s' % (resp.code, resp.code_msg)) + + cmd += '\000' + self.send_update(req_id, '200', '', cmd) + + resp = self.read_response() + if resp.code != '200': + raise RuntimeError('Failed to send-cmd: %s %s' % (resp.code, resp.code_msg)) + + self.send_update(req_id, SC_PROC_DONE, SS_PROC_DONE, '') + + def prepare_recv_message(self, msg_type): + request = 'mq-server %s' % msg_type + req_id = self.get_request_id() + self.send_request(req_id, request) + + resp = self.read_response() + if resp.code != '200': + raise RuntimeError('bad response: %s %s' % (resp.code, resp.code_msg)) + + def receive_message(self): + resp = self.read_response() + # the message from ccnet daemon has the trailing null byte included + msg = message_from_string(resp.content[:-1]) + return msg + + def prepare_send_message(self): + request = 'mq-server' + mq_req_id = self.get_request_id() + self.send_request(mq_req_id, request) + resp = self.read_response() + if resp.code != '200': + raise RuntimeError('bad response: %s %s' % (resp.code, resp.code_msg)) + self.mq_req_id = mq_req_id + + def send_message(self, msg_type, content): + if self.mq_req_id == -1: + self.prepare_send_message() + + msg = gen_inner_message_string(self.peer_id, msg_type, content) + self.send_update(self.mq_req_id, "300", '', msg) + resp = self.read_response() + if resp.code != '200': + self.mq_req_id = -1 + raise RuntimeError('bad response: %s %s' % (resp.code, resp.code_msg)) + + def register_service_sync(self, service, group): + '''Mainly used by a program to register a dummy service to ensure only + single instance of that program is running + + ''' + cmd = 'register-service %s %s' % (service, group) + self.send_cmd(cmd) diff --git a/python/ccnet/test-client.py b/python/ccnet/test-client.py new file mode 100644 index 0000000..2bb811b --- /dev/null +++ b/python/ccnet/test-client.py @@ -0,0 +1,62 @@ +import sys +import os +from ccnet import ClientPool, RpcClientBase +from pysearpc import searpc_func +import threading +import logging + +RPC_SERVICE_NAME = 'test-rpcserver' +CCNET_CONF_DIR = os.path.expanduser('~/.ccnet') + +class TestRpcClient(RpcClientBase): + def __init__(self, client_pool, *args, **kwargs): + RpcClientBase.__init__(self, client_pool, RPC_SERVICE_NAME, *args, **kwargs) + + @searpc_func('string', ['string', 'int']) + def str_mul(self, s, n): + pass + + +class Worker(threading.Thread): + def __init__(self, rpc): + threading.Thread.__init__(self) + self.rpc = rpc + + def run(self): + s = 'abcdef' + n = 100 + assert self.rpc.str_mul(s, n) == s * n + +def test(n): + rpcclient = TestRpcClient(ClientPool(CCNET_CONF_DIR, CCNET_CONF_DIR)) + + workers = [] + for i in xrange(n): + t = Worker(rpcclient) + t.start() + workers.append(t) + + for t in workers: + t.join() + +def setup_logging(): + kw = { + 'format': '[%(asctime)s][%(module)s]: %(message)s', + 'datefmt': '%m/%d/%Y %H:%M:%S', + 'level': logging.DEBUG, + 'stream': sys.stdout, + } + + logging.basicConfig(**kw) + +def main(): + setup_logging() + if len(sys.argv) > 1: + test(int(sys.argv[1])) + else: + test(100) + + print 'test passed' + +if __name__ == '__main__': + main() diff --git a/python/ccnet/test-server.py b/python/ccnet/test-server.py new file mode 100644 index 0000000..d69e28a --- /dev/null +++ b/python/ccnet/test-server.py @@ -0,0 +1,45 @@ +import os +import logging +import libevent + +from pysearpc import searpc_server +from ccnet.async import AsyncClient, RpcServerProc + +RPC_SERVICE_NAME = 'test-rpcserver' +CCNET_CONF_DIR = os.path.expanduser('~/.ccnet') + +def init_logging(): + """Configure logging module""" + level = logging.DEBUG + + kw = { + 'format': '[%(asctime)s] %(message)s', + 'datefmt': '%m/%d/%Y %H:%M:%S', + 'level': level, + } + + logging.basicConfig(**kw) + +i = 0 +def register_rpc_functions(session): + def str_mul(a, b): + global i + i = i + 1 + print '[%s] a = %s, b = %s' % (i, a, b) + return a * b + + searpc_server.create_service(RPC_SERVICE_NAME) + searpc_server.register_function(RPC_SERVICE_NAME, str_mul) + + session.register_service(RPC_SERVICE_NAME, 'basic', RpcServerProc) + +def main(): + init_logging() + evbase = libevent.Base() + session = AsyncClient(CCNET_CONF_DIR, evbase, CCNET_CONF_DIR) + session.connect_daemon() + register_rpc_functions(session) + session.main_loop() + +if __name__ == '__main__': + main() diff --git a/python/ccnet/utils.py b/python/ccnet/utils.py new file mode 100644 index 0000000..cc2f51d --- /dev/null +++ b/python/ccnet/utils.py @@ -0,0 +1,45 @@ +import os +import socket + +from ccnet.errors import NetworkError + +def recvall(fd, total): + remain = total + data = '' + while remain > 0: + try: + new = fd.recv(remain) + except socket.error as e: + raise NetworkError('Failed to read from socket: %s' % e) + + n = len(new) + if n <= 0: + raise NetworkError("Failed to read from socket") + else: + data += new + remain -= n + + return data + +def sendall(fd, data): + total = len(data) + offset = 0 + while offset < total: + try: + n = fd.send(data[offset:]) + except socket.error as e: + raise NetworkError('Failed to write to socket: %s' % e) + + if n <= 0: + raise NetworkError('Failed to write to socket') + else: + offset += n + +def is_win32(): + return os.name == 'nt' + +def make_socket_closeonexec(fd): + if not is_win32(): + import fcntl + old_flags = fcntl.fcntl(fd, fcntl.F_GETFD) + fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC) diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 0000000..61d0482 --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,12 @@ + +AM_CPPFLAGS = -I$(top_srcdir)/include @GLIB2_CFLAGS@ -I$(top_srcdir)/lib + +bin_PROGRAMS = ccnet-init + +ccnet_init_SOURCES = ccnet-init.c + +ccnet_init_LDADD = @SSL_LIBS@ @LIB_RESOLV@ @GLIB2_LIBS@ \ + $(top_builddir)/lib/libccnetd.la + +ccnet_init_LDFLAGS = @CONSOLE@ + diff --git a/tools/ccnet-admin.py b/tools/ccnet-admin.py new file mode 100755 index 0000000..b25fa1f --- /dev/null +++ b/tools/ccnet-admin.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# encoding: utf-8 + +import sqlite3 +import os +import sys +import time +import hashlib +import getpass + +# Get .ccnet directory from argument or user input +if len(sys.argv) >= 2: + ccnet_dir = sys.argv[1] +else: + home_dir = os.path.join(os.path.expanduser('~'), '.ccnet') + ccnet_dir = raw_input("Enter ccnet directory:(leave blank for %s) " % home_dir) + if not ccnet_dir: + ccnet_dir = home_dir + +# Test usermgr.db exists +usermgr_db = os.path.join(ccnet_dir, 'PeerMgr/usermgr.db') +if not os.path.exists(usermgr_db): + print '%s NOT exists. FAILED' % usermgr_db + sys.exit(1) + +# Connect db +conn = sqlite3.connect(usermgr_db) + +# Get cursor +c = conn.cursor() + +# Check whether admin user exists +sql = "SELECT email FROM EmailUser WHERE is_staff = 1" +try: + c.execute(sql) +except sqlite3.Error, e: + print "An error orrured:", e.args[0] + sys.exit(1) + +staff_list = c.fetchall() +if staff_list: + print "Admin is already in database. Email as follows: " + print '--------------------' + for e in staff_list: + print e[0] + print '--------------------' + choice = raw_input('Previous admin would be deleted, would you like to continue?[y/n] ') + if choice == 'y': + sql = "DELETE FROM EmailUser WHERE is_staff = 1" + try: + c.execute(sql) + except sqlite3.Error, e: + print "An error orrured:", e.args[0] + sys.exit(1) + else: + print 'Previous admin is deleted.' + else: + conn.close() + sys.exit(0) + +# Create admin user +choice = raw_input('Would you like to create admin user?[y/n]') +if choice != 'y': + conn.close() + sys.exit(0) + +username = raw_input('E-mail address:') +passwd = getpass.getpass('Password:') +passwd2 = getpass.getpass('Password (again):') +if passwd != passwd2: + print "Two passwords NOT same." + sys.exit(1) + +mySha1 = hashlib.sha1() +mySha1.update(passwd) +enc_passwd = mySha1.hexdigest() +sql = "INSERT INTO EmailUser(email, passwd, is_staff, is_active, ctime) VALUES ('%s', '%s', 1, 1, '%d');" % (username, enc_passwd, time.time()*1000000) +try: + c = conn.cursor() + c.execute(sql) + conn.commit() +except sqlite3.Error, e: + print "An error occured:", e.args[0] + sys.exit(1) +else: + print "Admin user created successfully." + +# Close db +conn.close() + diff --git a/tools/ccnet-init.c b/tools/ccnet-init.c new file mode 100644 index 0000000..229c993 --- /dev/null +++ b/tools/ccnet-init.c @@ -0,0 +1,258 @@ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "rsa.h" +#include "utils.h" + +enum { + ERR_NAME_NULL = 1, + ERR_NAME_INVALID, + ERR_PERMISSION, + ERR_CONF_FILE, +}; + +/* Number of bits in the RSA/DSA key. This value can be set on the command line. */ +#define DEFAULT_BITS 2048 +static guint32 bits = 0; + +static int quiet = 0; + +static char *identity_file_peer = NULL; + +static RSA *peer_privkey = NULL; +static RSA *peer_pubkey = NULL; + +static char *user_name = NULL; +static char *peer_name = NULL; +static char *peer_id = NULL; +static char *host_str = NULL; +static char *port_str = NULL; + +/* argv0 */ +static char *program_name = NULL; + + +static void make_configure_file (const char *config_file); + +void +save_privkey (RSA *key, const char *file) +{ + FILE *f; + f = g_fopen (file, "wb"); + PEM_write_RSAPrivateKey(f, key, NULL, NULL, 0, NULL, NULL); + fclose (f); +} + +static void +create_peerkey () +{ + peer_privkey = generate_private_key (bits); + peer_pubkey = private_key_to_pub (peer_privkey); +} + + +static const char *short_opts = "hc:n:H:P:F:"; +static const struct option long_opts[] = { + { "help", no_argument, NULL, 'h' }, + { "config-dir", required_argument, NULL, 'c' }, + { "central-config-dir", required_argument, NULL, 'F' }, + { "name", required_argument, NULL, 'n' }, + { "host", required_argument, NULL, 'H' }, + { "port", required_argument, NULL, 'P' }, + { 0, 0, 0, 0 }, +}; + +void usage (int exit_status) +{ + printf ("Usage: %s [OPTION]...\n", program_name); + + fputs ("Init ccnet configuration directory.\n\n", stdout); + + fputs ("Mandatory arguments to long options are mandatory " + "for short options too.\n", stdout); + + fputs ("" +" -c, --config-dir=DIR use DIR as the output ccnet configuration\n" +" directory. Default is ~/.ccnet\n" + , stdout); + fputs ("" +" -n, --name=NAME your public name\n" + , stdout); + fputs ("" +" -H, --host= Public addr. Only useful for server.\n" + , stdout); + fputs ("" +" -P, --port=port Public port. Only useful for server.\n" +" Default 10001.\n" + , stdout); + + exit (exit_status); +} + +static int +is_valid_username (const char *name) +{ + const char *p = name; + while (*p) { + if (!isalnum(*p) && *p != '_' && *p != '-') + return 0; + ++p; + } + + return 1; +} + +int +main(int argc, char **argv) +{ + char *config_dir; + char *central_config_dir = NULL; + char *config_file; + int c; + char *name = NULL; + + program_name = argv[0]; + + config_dir = DEFAULT_CONFIG_DIR; + + while ((c = getopt_long(argc, argv, + short_opts, long_opts, NULL)) != EOF) { + switch (c) { + case 'h': + usage (1); + break; + case 'F': + central_config_dir = strdup(optarg); + break; + case 'c': + config_dir = strdup(optarg); + break; + case 'n': + name = strdup (optarg); + break; + case 'H': + host_str = strdup (optarg); + break; + case 'P': + port_str = strdup (optarg); + break; + default: + usage(1); + } + } + + config_dir = ccnet_expand_path (config_dir); + /* printf("[conf_dir=%s\n]", config_dir); */ + SSLeay_add_all_algorithms(); + + if (RAND_status() != 1) { /* it should be seeded automatically */ + fprintf(stderr, "PRNG is not seeded\n"); + exit (1); + } + + if (bits == 0) + bits = DEFAULT_BITS; + + /* create peer key */ + if (!name) { + usage(-ERR_NAME_NULL); + } + if (strlen(name) < 2 || strlen (name) > 16 + || !is_valid_username(name)) { + fprintf (stderr, "The user name should be more than 2 bytes and less than 16 bytes, only digits, alphabetes and '-', '_' are allowed"); + exit(-ERR_NAME_INVALID); + } + + user_name = name; + peer_name = g_strdup (name); + + create_peerkey (); + peer_id = id_from_pubkey (peer_pubkey); + identity_file_peer = g_build_filename (config_dir, PEER_KEYFILE, NULL); + + /* create dir */ + if (ccnet_mkdir(config_dir, 0700) < 0) { + fprintf (stderr, "Make dir %s error: %s\n", + config_dir, strerror(errno)); + exit(-ERR_PERMISSION); + } + + struct stat st; + if (central_config_dir && g_stat(central_config_dir, &st) < 0 && + ccnet_mkdir(central_config_dir, 0700) < 0) { + fprintf(stderr, "Make dir %s error: %s\n", central_config_dir, + strerror(errno)); + exit(-ERR_PERMISSION); + } + + /* save key */ + save_privkey (peer_privkey, identity_file_peer); + + /* make configure file */ + config_file = g_build_filename (central_config_dir ? central_config_dir : config_dir, CONFIG_FILE_NAME, NULL); + make_configure_file (config_file); + + printf ("Successly create configuration dir %s.\n", config_dir); + exit(0); +} + + +static void +make_configure_file (const char *config_file) +{ + FILE *fp; + + if ((fp = g_fopen(config_file, "wb")) == NULL) { + fprintf (stderr, "Open config file %s error: %s\n", + config_file, strerror(errno)); + exit(-ERR_CONF_FILE); + } + + fprintf (fp, "[General]\n"); + fprintf (fp, "USER_NAME = %s\n", user_name); + fprintf (fp, "ID = %s\n", peer_id); + fprintf (fp, "NAME = %s\n", peer_name); + if (host_str) + fprintf (fp, "SERVICE_URL = http://%s:8000\n", host_str); + + if (port_str) { + fprintf (fp, "\n"); + fprintf (fp, "[Network]\n"); + fprintf (fp, "PORT = %s\n", port_str); + } + + fprintf (fp, "\n"); + fprintf (fp, "[Client]\n"); + + /* Use differnet ports for ccnet-daemon and ccnet-server */ + if (port_str != NULL) { + /* ccnet-server */ + fprintf (fp, "PORT = 13418\n"); + } else { + /* ccnet-daemon */ + fprintf (fp, "PORT = 13419\n"); + } + + fclose (fp); + + fprintf (stdout, "done\n"); +}